@aztec/p2p 5.0.0-private.20260319 → 5.0.0-rc.2

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 (411) hide show
  1. package/README.md +4 -1
  2. package/dest/bootstrap/bootstrap.d.ts +1 -1
  3. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  4. package/dest/bootstrap/bootstrap.js +22 -6
  5. package/dest/client/factory.d.ts +4 -3
  6. package/dest/client/factory.d.ts.map +1 -1
  7. package/dest/client/factory.js +33 -24
  8. package/dest/client/interface.d.ts +43 -11
  9. package/dest/client/interface.d.ts.map +1 -1
  10. package/dest/client/p2p_client.d.ts +33 -19
  11. package/dest/client/p2p_client.d.ts.map +1 -1
  12. package/dest/client/p2p_client.js +100 -44
  13. package/dest/config.d.ts +137 -107
  14. package/dest/config.d.ts.map +1 -1
  15. package/dest/config.js +64 -23
  16. package/dest/errors/p2p-service.error.d.ts +9 -0
  17. package/dest/errors/p2p-service.error.d.ts.map +1 -0
  18. package/dest/errors/p2p-service.error.js +10 -0
  19. package/dest/errors/reqresp.error.d.ts +1 -20
  20. package/dest/errors/reqresp.error.d.ts.map +1 -1
  21. package/dest/errors/reqresp.error.js +0 -21
  22. package/dest/index.d.ts +1 -2
  23. package/dest/index.d.ts.map +1 -1
  24. package/dest/index.js +0 -1
  25. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +98 -56
  26. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  27. package/dest/mem_pools/attestation_pool/attestation_pool.js +265 -196
  28. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +1 -1
  29. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  30. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +175 -59
  31. package/dest/mem_pools/attestation_pool/mocks.d.ts +1 -1
  32. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  33. package/dest/mem_pools/attestation_pool/mocks.js +6 -4
  34. package/dest/mem_pools/index.d.ts +1 -2
  35. package/dest/mem_pools/index.d.ts.map +1 -1
  36. package/dest/mem_pools/instrumentation.d.ts +1 -1
  37. package/dest/mem_pools/instrumentation.d.ts.map +1 -1
  38. package/dest/mem_pools/instrumentation.js +17 -1
  39. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts +1 -5
  40. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts.map +1 -1
  41. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.js +1 -7
  42. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +3 -3
  43. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -1
  44. package/dest/mem_pools/tx_pool_v2/deleted_pool.js +6 -6
  45. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +1 -1
  46. package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts +2 -1
  47. package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts.map +1 -1
  48. package/dest/mem_pools/tx_pool_v2/eviction/index.js +1 -0
  49. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts +16 -0
  50. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts.map +1 -0
  51. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.js +62 -0
  52. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +2 -2
  53. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +22 -5
  54. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
  55. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +5 -2
  56. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
  57. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +13 -6
  58. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +7 -1
  59. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -1
  60. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +10 -0
  61. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +8 -3
  62. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
  63. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +7 -4
  64. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +8 -3
  65. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
  66. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +153 -111
  67. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +10 -3
  68. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
  69. package/dest/msg_validators/attestation_validator/attestation_validator.js +38 -16
  70. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +7 -3
  71. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
  72. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +4 -5
  73. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +8 -3
  74. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
  75. package/dest/msg_validators/proposal_validator/block_proposal_validator.js +2 -2
  76. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +8 -3
  77. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
  78. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +8 -2
  79. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +25 -3
  80. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
  81. package/dest/msg_validators/proposal_validator/proposal_validator.js +77 -14
  82. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +1 -1
  83. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
  84. package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +8 -15
  85. package/dest/msg_validators/tx_validator/allowed_public_setup.js +3 -3
  86. package/dest/msg_validators/tx_validator/archive_cache.js +1 -1
  87. package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts +15 -0
  88. package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts.map +1 -0
  89. package/dest/msg_validators/tx_validator/cached_tx_validator.js +19 -0
  90. package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts +9 -0
  91. package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts.map +1 -0
  92. package/dest/msg_validators/tx_validator/contract_instance_validator.js +48 -0
  93. package/dest/msg_validators/tx_validator/data_validator.d.ts +2 -1
  94. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
  95. package/dest/msg_validators/tx_validator/data_validator.js +36 -2
  96. package/dest/msg_validators/tx_validator/factory.d.ts +14 -15
  97. package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
  98. package/dest/msg_validators/tx_validator/factory.js +36 -17
  99. package/dest/msg_validators/tx_validator/gas_validator.d.ts +47 -10
  100. package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
  101. package/dest/msg_validators/tx_validator/gas_validator.js +65 -48
  102. package/dest/msg_validators/tx_validator/index.d.ts +3 -1
  103. package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
  104. package/dest/msg_validators/tx_validator/index.js +2 -0
  105. package/dest/msg_validators/tx_validator/phases_validator.js +3 -3
  106. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +2 -1
  107. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -1
  108. package/dest/msg_validators/tx_validator/tx_proof_validator.js +2 -0
  109. package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts +48 -0
  110. package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts.map +1 -0
  111. package/dest/msg_validators/tx_validator/tx_validation_cache.js +68 -0
  112. package/dest/services/data_store.d.ts +1 -1
  113. package/dest/services/data_store.d.ts.map +1 -1
  114. package/dest/services/data_store.js +5 -5
  115. package/dest/services/discv5/discV5_service.d.ts +13 -2
  116. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  117. package/dest/services/discv5/discV5_service.js +119 -23
  118. package/dest/services/discv5/persisted_enr_store.d.ts +38 -0
  119. package/dest/services/discv5/persisted_enr_store.d.ts.map +1 -0
  120. package/dest/services/discv5/persisted_enr_store.js +145 -0
  121. package/dest/services/dummy_service.d.ts +15 -15
  122. package/dest/services/dummy_service.d.ts.map +1 -1
  123. package/dest/services/dummy_service.js +15 -16
  124. package/dest/services/encoding.d.ts +8 -8
  125. package/dest/services/encoding.d.ts.map +1 -1
  126. package/dest/services/encoding.js +11 -15
  127. package/dest/services/gossipsub/topic_score_params.d.ts +6 -12
  128. package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -1
  129. package/dest/services/gossipsub/topic_score_params.js +4 -14
  130. package/dest/services/libp2p/instrumentation.d.ts +3 -1
  131. package/dest/services/libp2p/instrumentation.d.ts.map +1 -1
  132. package/dest/services/libp2p/instrumentation.js +14 -0
  133. package/dest/services/libp2p/libp2p_service.d.ts +44 -39
  134. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  135. package/dest/services/libp2p/libp2p_service.js +383 -278
  136. package/dest/services/peer-manager/peer_manager.d.ts +20 -3
  137. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  138. package/dest/services/peer-manager/peer_manager.js +79 -23
  139. package/dest/services/peer-manager/peer_scoring.d.ts +34 -2
  140. package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
  141. package/dest/services/peer-manager/peer_scoring.js +102 -13
  142. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +1 -8
  143. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
  144. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +38 -44
  145. package/dest/services/reqresp/batch-tx-requester/interface.d.ts +8 -5
  146. package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -1
  147. package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts +5 -14
  148. package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts.map +1 -1
  149. package/dest/services/reqresp/batch-tx-requester/tx_validator.js +6 -20
  150. package/dest/services/reqresp/config.d.ts +3 -3
  151. package/dest/services/reqresp/config.d.ts.map +1 -1
  152. package/dest/services/reqresp/interface.d.ts +16 -18
  153. package/dest/services/reqresp/interface.d.ts.map +1 -1
  154. package/dest/services/reqresp/interface.js +10 -20
  155. package/dest/services/reqresp/metrics.d.ts +1 -1
  156. package/dest/services/reqresp/metrics.d.ts.map +1 -1
  157. package/dest/services/reqresp/metrics.js +0 -1
  158. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +4 -3
  159. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -1
  160. package/dest/services/reqresp/protocols/block_txs/bitvector.js +3 -2
  161. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +1 -1
  162. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
  163. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +31 -28
  164. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +12 -7
  165. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
  166. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +42 -24
  167. package/dest/services/reqresp/protocols/index.d.ts +1 -2
  168. package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
  169. package/dest/services/reqresp/protocols/index.js +0 -1
  170. package/dest/services/reqresp/protocols/tx.d.ts +1 -1
  171. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
  172. package/dest/services/reqresp/protocols/tx.js +1 -3
  173. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +5 -4
  174. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
  175. package/dest/services/reqresp/rate-limiter/rate_limiter.js +10 -8
  176. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
  177. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
  178. package/dest/services/reqresp/rate-limiter/rate_limits.js +0 -10
  179. package/dest/services/reqresp/reqresp.d.ts +7 -29
  180. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  181. package/dest/services/reqresp/reqresp.js +26 -207
  182. package/dest/services/service.d.ts +25 -12
  183. package/dest/services/service.d.ts.map +1 -1
  184. package/dest/services/tx_collection/config.d.ts +2 -23
  185. package/dest/services/tx_collection/config.d.ts.map +1 -1
  186. package/dest/services/tx_collection/config.js +2 -55
  187. package/dest/services/tx_collection/file_store_tx_collection.d.ts +12 -28
  188. package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -1
  189. package/dest/services/tx_collection/file_store_tx_collection.js +43 -83
  190. package/dest/services/tx_collection/file_store_tx_source.d.ts +5 -4
  191. package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -1
  192. package/dest/services/tx_collection/file_store_tx_source.js +39 -29
  193. package/dest/services/tx_collection/index.d.ts +2 -3
  194. package/dest/services/tx_collection/index.d.ts.map +1 -1
  195. package/dest/services/tx_collection/index.js +0 -1
  196. package/dest/services/tx_collection/instrumentation.d.ts +1 -1
  197. package/dest/services/tx_collection/instrumentation.d.ts.map +1 -1
  198. package/dest/services/tx_collection/instrumentation.js +0 -2
  199. package/dest/services/tx_collection/tx_collection.d.ts +35 -51
  200. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
  201. package/dest/services/tx_collection/tx_collection.js +275 -119
  202. package/dest/services/tx_collection/tx_collection_sink.d.ts +1 -1
  203. package/dest/services/tx_collection/tx_collection_sink.js +2 -2
  204. package/dest/services/tx_collection/tx_source.d.ts +6 -5
  205. package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
  206. package/dest/services/tx_collection/tx_source.js +13 -8
  207. package/dest/services/tx_file_store/tx_file_store.d.ts +1 -3
  208. package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -1
  209. package/dest/services/tx_file_store/tx_file_store.js +4 -14
  210. package/dest/services/tx_provider.d.ts +3 -1
  211. package/dest/services/tx_provider.d.ts.map +1 -1
  212. package/dest/services/tx_provider.js +3 -0
  213. package/dest/test-helpers/make-test-p2p-clients.d.ts +1 -1
  214. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  215. package/dest/test-helpers/make-test-p2p-clients.js +5 -2
  216. package/dest/test-helpers/mock-pubsub.d.ts +23 -9
  217. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  218. package/dest/test-helpers/mock-pubsub.js +44 -44
  219. package/dest/test-helpers/reqresp-nodes.d.ts +4 -5
  220. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  221. package/dest/test-helpers/reqresp-nodes.js +16 -18
  222. package/dest/test-helpers/test_tx_provider.d.ts +3 -1
  223. package/dest/test-helpers/test_tx_provider.d.ts.map +1 -1
  224. package/dest/test-helpers/test_tx_provider.js +3 -0
  225. package/dest/test-helpers/testbench-utils.d.ts +14 -14
  226. package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
  227. package/dest/test-helpers/testbench-utils.js +25 -13
  228. package/dest/testbench/p2p_client_testbench_worker.d.ts +3 -5
  229. package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -1
  230. package/dest/testbench/p2p_client_testbench_worker.js +102 -41
  231. package/dest/testbench/worker_client_manager.d.ts +10 -6
  232. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  233. package/dest/testbench/worker_client_manager.js +51 -9
  234. package/dest/util.d.ts +11 -6
  235. package/dest/util.d.ts.map +1 -1
  236. package/dest/util.js +35 -14
  237. package/dest/versioning.d.ts +3 -6
  238. package/dest/versioning.d.ts.map +1 -1
  239. package/dest/versioning.js +3 -24
  240. package/package.json +16 -16
  241. package/src/bootstrap/bootstrap.ts +17 -3
  242. package/src/client/factory.ts +52 -24
  243. package/src/client/interface.ts +38 -17
  244. package/src/client/p2p_client.ts +118 -76
  245. package/src/client/test/{tx_proposal_collector/README.md → p2p_client.batch_tx_requester.bench.README.md} +23 -53
  246. package/src/config.ts +130 -31
  247. package/src/errors/p2p-service.error.ts +11 -0
  248. package/src/errors/reqresp.error.ts +0 -25
  249. package/src/index.ts +0 -1
  250. package/src/mem_pools/attestation_pool/attestation_pool.ts +319 -238
  251. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +198 -62
  252. package/src/mem_pools/attestation_pool/mocks.ts +13 -8
  253. package/src/mem_pools/index.ts +0 -3
  254. package/src/mem_pools/instrumentation.ts +5 -1
  255. package/src/mem_pools/tx_pool_v2/archive/tx_archive.ts +1 -9
  256. package/src/mem_pools/tx_pool_v2/deleted_pool.ts +7 -7
  257. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +1 -1
  258. package/src/mem_pools/tx_pool_v2/eviction/index.ts +1 -0
  259. package/src/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.ts +65 -0
  260. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +3 -3
  261. package/src/mem_pools/tx_pool_v2/interfaces.ts +18 -4
  262. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +20 -8
  263. package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +12 -0
  264. package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +8 -4
  265. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +163 -117
  266. package/src/msg_validators/attestation_validator/attestation_validator.ts +46 -15
  267. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +15 -7
  268. package/src/msg_validators/proposal_validator/README.md +1 -1
  269. package/src/msg_validators/proposal_validator/block_proposal_validator.ts +15 -3
  270. package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +26 -3
  271. package/src/msg_validators/proposal_validator/proposal_validator.ts +89 -11
  272. package/src/msg_validators/tx_validator/README.md +13 -3
  273. package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +3 -12
  274. package/src/msg_validators/tx_validator/allowed_public_setup.ts +3 -3
  275. package/src/msg_validators/tx_validator/archive_cache.ts +1 -1
  276. package/src/msg_validators/tx_validator/cached_tx_validator.ts +31 -0
  277. package/src/msg_validators/tx_validator/contract_instance_validator.ts +56 -0
  278. package/src/msg_validators/tx_validator/data_validator.ts +44 -1
  279. package/src/msg_validators/tx_validator/factory.ts +44 -20
  280. package/src/msg_validators/tx_validator/gas_validator.ts +99 -54
  281. package/src/msg_validators/tx_validator/index.ts +2 -0
  282. package/src/msg_validators/tx_validator/phases_validator.ts +3 -3
  283. package/src/msg_validators/tx_validator/tx_proof_validator.ts +2 -0
  284. package/src/msg_validators/tx_validator/tx_validation_cache.ts +101 -0
  285. package/src/services/data_store.ts +5 -13
  286. package/src/services/discv5/discV5_service.ts +126 -14
  287. package/src/services/discv5/persisted_enr_store.ts +158 -0
  288. package/src/services/dummy_service.ts +20 -39
  289. package/src/services/encoding.ts +11 -21
  290. package/src/services/gossipsub/README.md +33 -11
  291. package/src/services/gossipsub/topic_score_params.ts +9 -18
  292. package/src/services/libp2p/instrumentation.ts +14 -0
  293. package/src/services/libp2p/libp2p_service.ts +436 -311
  294. package/src/services/peer-manager/peer_manager.ts +86 -22
  295. package/src/services/peer-manager/peer_scoring.ts +102 -8
  296. package/src/services/reqresp/README.md +0 -14
  297. package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +42 -52
  298. package/src/services/reqresp/batch-tx-requester/interface.ts +11 -4
  299. package/src/services/reqresp/batch-tx-requester/tx_validator.ts +12 -25
  300. package/src/services/reqresp/config.ts +2 -2
  301. package/src/services/reqresp/interface.ts +21 -47
  302. package/src/services/reqresp/metrics.ts +0 -1
  303. package/src/services/reqresp/protocols/block_txs/bitvector.ts +3 -2
  304. package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +34 -30
  305. package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +47 -20
  306. package/src/services/reqresp/protocols/index.ts +0 -1
  307. package/src/services/reqresp/protocols/tx.ts +1 -3
  308. package/src/services/reqresp/rate-limiter/rate_limiter.ts +13 -9
  309. package/src/services/reqresp/rate-limiter/rate_limits.ts +0 -10
  310. package/src/services/reqresp/reqresp.ts +29 -252
  311. package/src/services/service.ts +31 -28
  312. package/src/services/tx_collection/config.ts +3 -80
  313. package/src/services/tx_collection/file_store_tx_collection.ts +54 -103
  314. package/src/services/tx_collection/file_store_tx_source.ts +43 -31
  315. package/src/services/tx_collection/index.ts +1 -6
  316. package/src/services/tx_collection/instrumentation.ts +1 -7
  317. package/src/services/tx_collection/tx_collection.ts +330 -173
  318. package/src/services/tx_collection/tx_collection_sink.ts +2 -2
  319. package/src/services/tx_collection/tx_source.ts +10 -8
  320. package/src/services/tx_file_store/tx_file_store.ts +5 -17
  321. package/src/services/tx_provider.ts +5 -0
  322. package/src/test-helpers/make-test-p2p-clients.ts +3 -0
  323. package/src/test-helpers/mock-pubsub.ts +46 -60
  324. package/src/test-helpers/reqresp-nodes.ts +12 -22
  325. package/src/test-helpers/test_tx_provider.ts +5 -0
  326. package/src/test-helpers/testbench-utils.ts +31 -26
  327. package/src/testbench/p2p_client_testbench_worker.ts +110 -55
  328. package/src/testbench/worker_client_manager.ts +59 -20
  329. package/src/util.ts +32 -17
  330. package/src/versioning.ts +3 -33
  331. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts +0 -2
  332. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts.map +0 -1
  333. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +0 -305
  334. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts +0 -73
  335. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts.map +0 -1
  336. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.js +0 -8
  337. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +0 -125
  338. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +0 -1
  339. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +0 -596
  340. package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts +0 -32
  341. package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts.map +0 -1
  342. package/dest/mem_pools/tx_pool/eviction/eviction_manager.js +0 -112
  343. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts +0 -157
  344. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts.map +0 -1
  345. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.js +0 -52
  346. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts +0 -16
  347. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts.map +0 -1
  348. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.js +0 -123
  349. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts +0 -17
  350. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts.map +0 -1
  351. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.js +0 -84
  352. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts +0 -19
  353. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts.map +0 -1
  354. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.js +0 -78
  355. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts +0 -26
  356. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts.map +0 -1
  357. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.js +0 -84
  358. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts +0 -25
  359. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts.map +0 -1
  360. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.js +0 -57
  361. package/dest/mem_pools/tx_pool/index.d.ts +0 -3
  362. package/dest/mem_pools/tx_pool/index.d.ts.map +0 -1
  363. package/dest/mem_pools/tx_pool/index.js +0 -2
  364. package/dest/mem_pools/tx_pool/priority.d.ts +0 -12
  365. package/dest/mem_pools/tx_pool/priority.d.ts.map +0 -1
  366. package/dest/mem_pools/tx_pool/priority.js +0 -15
  367. package/dest/mem_pools/tx_pool/tx_pool.d.ts +0 -127
  368. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +0 -1
  369. package/dest/mem_pools/tx_pool/tx_pool.js +0 -3
  370. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +0 -7
  371. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +0 -1
  372. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +0 -402
  373. package/dest/msg_validators/clock_tolerance.d.ts +0 -21
  374. package/dest/msg_validators/clock_tolerance.d.ts.map +0 -1
  375. package/dest/msg_validators/clock_tolerance.js +0 -38
  376. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +0 -64
  377. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +0 -1
  378. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.js +0 -151
  379. package/dest/services/reqresp/protocols/block.d.ts +0 -9
  380. package/dest/services/reqresp/protocols/block.d.ts.map +0 -1
  381. package/dest/services/reqresp/protocols/block.js +0 -32
  382. package/dest/services/tx_collection/fast_tx_collection.d.ts +0 -51
  383. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +0 -1
  384. package/dest/services/tx_collection/fast_tx_collection.js +0 -311
  385. package/dest/services/tx_collection/proposal_tx_collector.d.ts +0 -48
  386. package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +0 -1
  387. package/dest/services/tx_collection/proposal_tx_collector.js +0 -50
  388. package/dest/services/tx_collection/slow_tx_collection.d.ts +0 -57
  389. package/dest/services/tx_collection/slow_tx_collection.d.ts.map +0 -1
  390. package/dest/services/tx_collection/slow_tx_collection.js +0 -211
  391. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +0 -344
  392. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.ts +0 -43
  393. package/src/mem_pools/tx_pool/README.md +0 -270
  394. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +0 -746
  395. package/src/mem_pools/tx_pool/eviction/eviction_manager.ts +0 -132
  396. package/src/mem_pools/tx_pool/eviction/eviction_strategy.ts +0 -208
  397. package/src/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.ts +0 -163
  398. package/src/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.ts +0 -104
  399. package/src/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.ts +0 -93
  400. package/src/mem_pools/tx_pool/eviction/low_priority_eviction_rule.ts +0 -106
  401. package/src/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.ts +0 -75
  402. package/src/mem_pools/tx_pool/index.ts +0 -2
  403. package/src/mem_pools/tx_pool/priority.ts +0 -20
  404. package/src/mem_pools/tx_pool/tx_pool.ts +0 -141
  405. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +0 -321
  406. package/src/msg_validators/clock_tolerance.ts +0 -52
  407. package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +0 -161
  408. package/src/services/reqresp/protocols/block.ts +0 -37
  409. package/src/services/tx_collection/fast_tx_collection.ts +0 -361
  410. package/src/services/tx_collection/proposal_tx_collector.ts +0 -108
  411. package/src/services/tx_collection/slow_tx_collection.ts +0 -266
@@ -1,15 +1,15 @@
1
1
  import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
2
  import { BlockNumber, type SlotNumber } from '@aztec/foundation/branded-types';
3
- import { maxBy } from '@aztec/foundation/collection';
4
- import { Fr } from '@aztec/foundation/curves/bn254';
3
+ import { compactArray, maxBy, merge } from '@aztec/foundation/collection';
5
4
  import { type Logger, createLibp2pComponentLogger, createLogger } from '@aztec/foundation/log';
6
5
  import { RunningPromise } from '@aztec/foundation/running-promise';
7
6
  import { Timer } from '@aztec/foundation/timer';
8
7
  import type { AztecAsyncKVStore } from '@aztec/kv-store';
9
8
  import { protocolContractsHash } from '@aztec/protocol-contracts';
10
- import type { EthAddress, L2Block, L2BlockSource } from '@aztec/stdlib/block';
9
+ import type { EthAddress, L2BlockSource } from '@aztec/stdlib/block';
10
+ import { DEFAULT_MAX_BLOCKS_PER_CHECKPOINT } from '@aztec/stdlib/config';
11
11
  import type { ContractDataSource } from '@aztec/stdlib/contract';
12
- import { GasFees } from '@aztec/stdlib/gas';
12
+ import { type BlockMinFeesProvider, GasFees, getNetworkTxGasLimits } from '@aztec/stdlib/gas';
13
13
  import type { ClientProtocolCircuitVerifier, PeerInfo, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
14
14
  import {
15
15
  BlockProposal,
@@ -25,8 +25,9 @@ import {
25
25
  getTopicsForConfig,
26
26
  metricsTopicStrToLabels,
27
27
  } from '@aztec/stdlib/p2p';
28
+ import { ConsensusTimetable, getDefaultCheckpointProposalSyncGrace } from '@aztec/stdlib/timetable';
28
29
  import { MerkleTreeId } from '@aztec/stdlib/trees';
29
- import { Tx, type TxHash, type TxValidationResult, type TxValidator } from '@aztec/stdlib/tx';
30
+ import { Tx, type TxValidationResult } from '@aztec/stdlib/tx';
30
31
  import type { UInt64 } from '@aztec/stdlib/types';
31
32
  import { compressComponentVersions } from '@aztec/stdlib/versioning';
32
33
  import {
@@ -51,13 +52,15 @@ import { yamux } from '@chainsafe/libp2p-yamux';
51
52
  import { bootstrap } from '@libp2p/bootstrap';
52
53
  import { identify } from '@libp2p/identify';
53
54
  import { type Message, type MultiaddrConnection, type PeerId, TopicValidatorResult } from '@libp2p/interface';
54
- import type { ConnectionManager } from '@libp2p/interface-internal';
55
+ import type { AddressManager, ConnectionManager } from '@libp2p/interface-internal';
55
56
  import { mplex } from '@libp2p/mplex';
56
57
  import { tcp } from '@libp2p/tcp';
58
+ import { multiaddr } from '@multiformats/multiaddr';
57
59
  import { ENR } from '@nethermindeth/enr';
58
60
  import { createLibp2p } from 'libp2p';
59
61
 
60
62
  import type { P2PConfig } from '../../config.js';
63
+ import { CheckpointProposalReceivedCallbackNotRegisteredError } from '../../errors/p2p-service.error.js';
61
64
  import type { MemPools } from '../../mem_pools/interface.js';
62
65
  import {
63
66
  BlockProposalValidator,
@@ -73,14 +76,14 @@ import {
73
76
  createFirstStageTxValidationsForGossipedTransactions,
74
77
  createSecondStageTxValidationsForGossipedTransactions,
75
78
  createTxValidatorForBlockProposalReceivedTxs,
76
- createTxValidatorForReqResponseReceivedTxs,
77
79
  } from '../../msg_validators/tx_validator/factory.js';
80
+ import { TxValidationCache } from '../../msg_validators/tx_validator/tx_validation_cache.js';
78
81
  import { GossipSubEvent } from '../../types/index.js';
79
82
  import { type PubSubLibp2p, convertToMultiaddr } from '../../util.js';
80
83
  import { getVersions } from '../../versioning.js';
81
84
  import { AztecDatastore } from '../data_store.js';
82
85
  import { DiscV5Service } from '../discv5/discV5_service.js';
83
- import { SnappyTransform, fastMsgIdFn, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
86
+ import { SnappyTransform, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
84
87
  import { APP_SPECIFIC_WEIGHT, gossipScoreThresholds } from '../gossipsub/scoring.js';
85
88
  import { createAllTopicScoreParams } from '../gossipsub/topic_score_params.js';
86
89
  import type { PeerManagerInterface } from '../peer-manager/interface.js';
@@ -92,19 +95,15 @@ import {
92
95
  AuthRequest,
93
96
  BlockTxsRequest,
94
97
  BlockTxsResponse,
95
- DEFAULT_SUB_PROTOCOL_VALIDATORS,
96
98
  type ReqRespInterface,
97
99
  type ReqRespResponse,
98
100
  ReqRespSubProtocol,
99
101
  type ReqRespSubProtocolHandler,
100
102
  type ReqRespSubProtocolHandlers,
101
- type ReqRespSubProtocolValidators,
102
103
  StatusMessage,
103
- type SubProtocolMap,
104
104
  ValidationError,
105
105
  pingHandler,
106
106
  reqGoodbyeHandler,
107
- reqRespBlockHandler,
108
107
  reqRespBlockTxsHandler,
109
108
  reqRespStatusHandler,
110
109
  reqRespTxHandler,
@@ -112,13 +111,33 @@ import {
112
111
  import { ReqResp } from '../reqresp/reqresp.js';
113
112
  import type {
114
113
  P2PBlockReceivedCallback,
114
+ P2PCheckpointAttestationCallback,
115
115
  P2PCheckpointReceivedCallback,
116
116
  P2PDuplicateAttestationCallback,
117
+ P2POversizedProposalCallback,
117
118
  P2PService,
118
119
  PeerDiscoveryService,
119
120
  } from '../service.js';
120
121
  import { P2PInstrumentation } from './instrumentation.js';
121
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
+
122
141
  interface ValidationResult {
123
142
  name: string;
124
143
  isValid: TxValidationResult;
@@ -130,7 +149,7 @@ type ValidationOutcome = { allPassed: true } | { allPassed: false; failure: Vali
130
149
  // REFACTOR: Unify with the type above
131
150
  type ReceivedMessageValidationResult<T, M = undefined> =
132
151
  | { obj: T; result: Exclude<TopicValidatorResult, TopicValidatorResult.Reject>; metadata?: M }
133
- | { obj?: T; result: TopicValidatorResult.Reject; metadata?: M };
152
+ | { obj?: T; result: TopicValidatorResult.Reject; metadata?: M; severity: PeerErrorSeverity };
134
153
 
135
154
  /**
136
155
  * Lib P2P implementation of the P2PService interface.
@@ -147,8 +166,6 @@ export class LibP2PService extends WithTracer implements P2PService {
147
166
  private protocolVersion = '';
148
167
  private topicStrings: Record<TopicType, string> = {} as Record<TopicType, string>;
149
168
 
150
- private feesCache: { blockNumber: BlockNumber; gasFees: GasFees } | undefined;
151
-
152
169
  /** Callback invoked when a duplicate proposal is detected (triggers slashing). */
153
170
  private duplicateProposalCallback?: (info: {
154
171
  slot: SlotNumber;
@@ -156,9 +173,15 @@ export class LibP2PService extends WithTracer implements P2PService {
156
173
  type: 'checkpoint' | 'block';
157
174
  }) => void;
158
175
 
176
+ /** Callback invoked when an oversized block proposal is stored as slashing evidence (triggers slashing). */
177
+ private oversizedProposalCallback?: P2POversizedProposalCallback;
178
+
159
179
  /** Callback invoked when a duplicate attestation is detected (triggers slashing). */
160
180
  private duplicateAttestationCallback?: P2PDuplicateAttestationCallback;
161
181
 
182
+ /** Callback invoked when a valid checkpoint attestation is accepted into the pool. */
183
+ private checkpointAttestationCallback?: P2PCheckpointAttestationCallback;
184
+
162
185
  /**
163
186
  * Callback for when a block is received from a peer.
164
187
  * @param block - The block received from the peer.
@@ -171,10 +194,19 @@ export class LibP2PService extends WithTracer implements P2PService {
171
194
  * @param checkpoint - The checkpoint proposal received from the peer.
172
195
  * @returns The attestations for the checkpoint, if any.
173
196
  */
174
- private checkpointReceivedCallback: P2PCheckpointReceivedCallback;
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;
175
204
 
176
205
  private gossipSubEventHandler: (e: CustomEvent<GossipsubMessage>) => void;
177
206
 
207
+ private ipChangedHandler?: (ip: string) => void;
208
+ private discoveredP2pIp?: string;
209
+
178
210
  private instrumentation: P2PInstrumentation;
179
211
 
180
212
  private telemetry: TelemetryClient;
@@ -192,8 +224,10 @@ export class LibP2PService extends WithTracer implements P2PService {
192
224
  private epochCache: EpochCacheInterface,
193
225
  private proofVerifier: ClientProtocolCircuitVerifier,
194
226
  private worldStateSynchronizer: WorldStateSynchronizer,
227
+ private blockMinFeesProvider: BlockMinFeesProvider,
195
228
  telemetry: TelemetryClient,
196
229
  logger: Logger = createLogger('p2p:libp2p_service'),
230
+ private txValidationCache?: TxValidationCache,
197
231
  ) {
198
232
  super(telemetry, 'LibP2PService');
199
233
  this.telemetry = telemetry;
@@ -223,15 +257,39 @@ export class LibP2PService extends WithTracer implements P2PService {
223
257
  this.protocolVersion,
224
258
  );
225
259
 
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());
226
263
  const proposalValidatorOpts = {
227
264
  txsPermitted: !config.disableTransactions,
228
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,
229
283
  };
230
- this.blockProposalValidator = new BlockProposalValidator(epochCache, proposalValidatorOpts);
231
- this.checkpointProposalValidator = new CheckpointProposalValidator(epochCache, proposalValidatorOpts);
232
284
  this.checkpointAttestationValidator = config.fishermanMode
233
- ? new FishermanAttestationValidator(epochCache, mempools.attestationPool, telemetry)
234
- : new CheckpointAttestationValidator(epochCache);
285
+ ? new FishermanAttestationValidator(
286
+ epochCache,
287
+ consensusTimetable,
288
+ mempools.attestationPool,
289
+ telemetry,
290
+ attestationValidatorOpts,
291
+ )
292
+ : new CheckpointAttestationValidator(epochCache, consensusTimetable, attestationValidatorOpts);
235
293
 
236
294
  this.gossipSubEventHandler = this.handleGossipSubEvent.bind(this);
237
295
 
@@ -243,18 +301,22 @@ export class LibP2PService extends WithTracer implements P2PService {
243
301
  return true;
244
302
  };
245
303
 
246
- this.checkpointReceivedCallback = (
247
- checkpoint: CheckpointProposalCore,
304
+ this.allNodesCheckpointReceivedCallback = (
305
+ _checkpoint: CheckpointProposalCore,
306
+ ): Promise<CheckpointAttestation[] | undefined> => {
307
+ throw new CheckpointProposalReceivedCallbackNotRegisteredError();
308
+ };
309
+
310
+ this.validatorCheckpointReceivedCallback = (
311
+ _checkpoint: CheckpointProposalCore,
248
312
  ): Promise<CheckpointAttestation[] | undefined> => {
249
- this.logger.debug(
250
- `Handler not yet registered: Checkpoint received callback not set. Received checkpoint for slot ${checkpoint.slotNumber} from peer.`,
251
- );
252
313
  return Promise.resolve(undefined);
253
314
  };
254
315
  }
255
316
 
256
- public updateConfig(config: Partial<P2PReqRespConfig>) {
317
+ public updateConfig(config: Partial<P2PReqRespConfig & Pick<P2PConfig, 'skipIncomingProposals'>>) {
257
318
  this.reqresp.updateConfig(config);
319
+ this.config = merge(this.config, config);
258
320
  }
259
321
 
260
322
  /**
@@ -273,9 +335,11 @@ export class LibP2PService extends WithTracer implements P2PService {
273
335
  proofVerifier: ClientProtocolCircuitVerifier;
274
336
  worldStateSynchronizer: WorldStateSynchronizer;
275
337
  peerStore: AztecAsyncKVStore;
338
+ blockMinFeesProvider: BlockMinFeesProvider;
276
339
  telemetry: TelemetryClient;
277
340
  logger: Logger;
278
341
  packageVersion: string;
342
+ txValidationCache?: TxValidationCache;
279
343
  },
280
344
  ) {
281
345
  const {
@@ -285,9 +349,11 @@ export class LibP2PService extends WithTracer implements P2PService {
285
349
  mempools,
286
350
  proofVerifier,
287
351
  peerStore,
352
+ blockMinFeesProvider,
288
353
  telemetry,
289
354
  logger,
290
355
  packageVersion,
356
+ txValidationCache,
291
357
  } = deps;
292
358
  const { p2pPort, maxPeerCount, listenAddress } = config;
293
359
  const bindAddrTcp = convertToMultiaddr(listenAddress, p2pPort, 'tcp');
@@ -302,6 +368,7 @@ export class LibP2PService extends WithTracer implements P2PService {
302
368
  packageVersion,
303
369
  telemetry,
304
370
  createLogger(`${logger.module}:discv5_service`, logger.getBindings()),
371
+ peerStore,
305
372
  );
306
373
 
307
374
  // Seed libp2p's bootstrap discovery with private and trusted peers
@@ -316,34 +383,49 @@ export class LibP2PService extends WithTracer implements P2PService {
316
383
  const protocolVersion = compressComponentVersions(versions);
317
384
 
318
385
  const preferredPeersEnrs: ENR[] = config.preferredPeers.map(enr => ENR.decodeTxt(enr));
319
- const directPeers = (
386
+ const directPeers = compactArray(
320
387
  await Promise.all(
321
388
  preferredPeersEnrs.map(async enr => {
322
- const peerId = await enr.peerId();
323
- const address = enr.getLocationMultiaddr('tcp');
324
- if (address === undefined) {
325
- 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;
326
403
  }
327
- return {
328
- id: peerId,
329
- addrs: [address],
330
- };
331
404
  }),
332
- )
333
- ).filter(peer => peer !== undefined);
405
+ ),
406
+ );
334
407
 
335
408
  const announceTcpMultiaddr = config.p2pIp ? [convertToMultiaddr(config.p2pIp, p2pPort, 'tcp')] : [];
336
409
 
337
- // Create dynamic topic score params based on network configuration
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.
338
413
  const l1Constants = epochCache.getL1Constants();
339
414
  const topicScoreParams = createAllTopicScoreParams(protocolVersion, {
340
415
  slotDurationMs: l1Constants.slotDuration * 1000,
341
416
  heartbeatIntervalMs: config.gossipsubInterval,
342
417
  targetCommitteeSize: l1Constants.targetCommitteeSize,
343
- blockDurationMs: config.blockDurationMs,
418
+ maxBlocksPerCheckpoint: config.maxBlocksPerCheckpoint ?? DEFAULT_MAX_BLOCKS_PER_CHECKPOINT,
344
419
  expectedBlockProposalsPerSlot: config.expectedBlockProposalsPerSlot,
345
420
  });
346
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
+
347
429
  const node = await createLibp2p({
348
430
  start: false,
349
431
  peerId,
@@ -389,7 +471,7 @@ export class LibP2PService extends WithTracer implements P2PService {
389
471
  },
390
472
  connectionGater: {
391
473
  denyInboundConnection: (maConn: MultiaddrConnection) => {
392
- const allowed = peerManager.isNodeAllowedToConnect(maConn.remoteAddr.nodeAddress().address);
474
+ const allowed = peerManager.isAddressAllowedToConnect(maConn.remoteAddr.nodeAddress().address);
393
475
  if (allowed) {
394
476
  return false;
395
477
  }
@@ -400,7 +482,7 @@ export class LibP2PService extends WithTracer implements P2PService {
400
482
  denyInboundEncryptedConnection: (peerId: PeerId, _maConn: MultiaddrConnection) => {
401
483
  //NOTE: it is not necessary to check address here because this was already done by
402
484
  // denyInboundConnection
403
- const allowed = peerManager.isNodeAllowedToConnect(peerId);
485
+ const allowed = peerManager.isPeerAllowedToConnect(peerId);
404
486
  if (allowed) {
405
487
  return false;
406
488
  }
@@ -428,9 +510,13 @@ export class LibP2PService extends WithTracer implements P2PService {
428
510
  mcacheLength: config.gossipsubMcacheLength,
429
511
  mcacheGossip: config.gossipsubMcacheGossip,
430
512
  seenTTL: config.gossipsubSeenTTL,
513
+ allowedTopics,
514
+ // No fastMsgIdFn: the fast-path dedup cache keys on a non-cryptographic 64-bit hash of the
515
+ // raw data only (no topic), so a collision — accidental or engineered via a weak seed — drops
516
+ // a different message with no fallback to the full id. Dedup instead rests solely on the
517
+ // cryptographic, topic-framed msgIdFn below.
431
518
  msgIdFn: getMsgIdFn,
432
519
  msgIdToStrFn: msgIdToStrFn,
433
- fastMsgIdFn: fastMsgIdFn,
434
520
  dataTransform: new SnappyTransform(),
435
521
  metricsRegister: otelMetricsAdapter,
436
522
  metricsTopicStrToLabel: metricsTopicStrToLabels(protocolVersion),
@@ -442,8 +528,9 @@ export class LibP2PService extends WithTracer implements P2PService {
442
528
  topics: topicScoreParams,
443
529
  }),
444
530
  }) as (components: GossipSubComponents) => GossipSub,
445
- components: (components: { connectionManager: ConnectionManager }) => ({
531
+ components: (components: { connectionManager: ConnectionManager; addressManager: AddressManager }) => ({
446
532
  connectionManager: components.connectionManager,
533
+ addressManager: components.addressManager,
447
534
  }),
448
535
  },
449
536
  logger: createLibp2pComponentLogger(logger.module, logger.getBindings()),
@@ -465,6 +552,9 @@ export class LibP2PService extends WithTracer implements P2PService {
465
552
  epochCache,
466
553
  );
467
554
 
555
+ // Gate req/resp data protocols for unauthenticated peers when p2pAllowOnlyValidators is enabled
556
+ reqresp.setShouldRejectPeer(peerId => peerManager.shouldDisableP2PGossip(peerId));
557
+
468
558
  // Configure application-specific scoring for gossipsub.
469
559
  // The weight scales app score to align with gossipsub thresholds:
470
560
  // - Disconnect (-50) × 10 = -500 = gossipThreshold (stops receiving gossip)
@@ -485,8 +575,10 @@ export class LibP2PService extends WithTracer implements P2PService {
485
575
  epochCache,
486
576
  proofVerifier,
487
577
  worldStateSynchronizer,
578
+ blockMinFeesProvider,
488
579
  telemetry,
489
580
  logger,
581
+ txValidationCache,
490
582
  );
491
583
  }
492
584
 
@@ -500,24 +592,21 @@ export class LibP2PService extends WithTracer implements P2PService {
500
592
  throw new Error('P2P service already started');
501
593
  }
502
594
 
503
- // Get listen & announce addresses for logging
504
595
  const { p2pIp, p2pPort } = this.config;
505
- if (!p2pIp) {
506
- throw new Error('Announce address not provided.');
596
+ if (!p2pIp && !this.config.queryForIp) {
597
+ throw new Error('Announce address not provided and queryForIp is not enabled.');
507
598
  }
508
- const announceTcpMultiaddr = convertToMultiaddr(p2pIp, p2pPort, 'tcp');
599
+ const announceTcpMultiaddr = p2pIp ? convertToMultiaddr(p2pIp, p2pPort, 'tcp') : undefined;
509
600
 
510
601
  // Create request response protocol handlers
511
602
  const txHandler = reqRespTxHandler(this.mempools);
512
603
  const goodbyeHandler = reqGoodbyeHandler(this.peerManager);
513
- const blockHandler = reqRespBlockHandler(this.archiver);
514
604
  const statusHandler = reqRespStatusHandler(this.protocolVersion, this.worldStateSynchronizer, this.logger);
515
605
 
516
606
  const requestResponseHandlers: Partial<ReqRespSubProtocolHandlers> = {
517
607
  [ReqRespSubProtocol.PING]: pingHandler,
518
608
  [ReqRespSubProtocol.STATUS]: statusHandler.bind(this),
519
609
  [ReqRespSubProtocol.GOODBYE]: goodbyeHandler.bind(this),
520
- [ReqRespSubProtocol.BLOCK]: blockHandler.bind(this),
521
610
  };
522
611
 
523
612
  if (!this.config.disableTransactions) {
@@ -533,17 +622,9 @@ export class LibP2PService extends WithTracer implements P2PService {
533
622
  requestResponseHandlers[ReqRespSubProtocol.TX] = txHandler.bind(this);
534
623
  }
535
624
 
536
- // Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
537
- const reqrespSubProtocolValidators = {
538
- ...DEFAULT_SUB_PROTOCOL_VALIDATORS,
539
- [ReqRespSubProtocol.TX]: this.validateRequestedTxs.bind(this),
540
- [ReqRespSubProtocol.BLOCK_TXS]: this.validateRequestedBlockTxs.bind(this),
541
- [ReqRespSubProtocol.BLOCK]: this.validateRequestedBlock.bind(this),
542
- };
543
-
544
625
  await this.peerManager.initializePeers();
545
626
 
546
- await this.reqresp.start(requestResponseHandlers, reqrespSubProtocolValidators);
627
+ await this.reqresp.start(requestResponseHandlers);
547
628
 
548
629
  await this.node.start();
549
630
 
@@ -559,6 +640,38 @@ export class LibP2PService extends WithTracer implements P2PService {
559
640
  if (!this.config.p2pDiscoveryDisabled) {
560
641
  await this.peerDiscoveryService.start();
561
642
  }
643
+
644
+ // Bridge discv5 IP changes to libp2p's AddressManager so peers see the updated address
645
+ if (this.config.queryForIp) {
646
+ this.discoveredP2pIp = this.config.p2pIp;
647
+ this.logger.info('IP change tracking enabled, bridging discv5 IP updates to libp2p AddressManager');
648
+ this.ipChangedHandler = (ip: string) => {
649
+ const addressManager = this.node.services.components.addressManager;
650
+ const newAddr = multiaddr(convertToMultiaddr(ip, this.config.p2pPort, 'tcp'));
651
+ const previousIp = this.discoveredP2pIp;
652
+
653
+ if (previousIp) {
654
+ const oldAddr = multiaddr(convertToMultiaddr(previousIp, this.config.p2pPort, 'tcp'));
655
+ addressManager.removeObservedAddr(oldAddr);
656
+ this.logger.info('Libp2p announce address updated due to IP change', {
657
+ previousIp,
658
+ newIp: ip,
659
+ newMultiaddr: newAddr.toString(),
660
+ });
661
+ } else {
662
+ this.logger.info('Libp2p announce address set from initial discv5 IP discovery', {
663
+ ip,
664
+ multiaddr: newAddr.toString(),
665
+ });
666
+ }
667
+
668
+ addressManager.addObservedAddr(newAddr);
669
+ addressManager.confirmObservedAddr(newAddr);
670
+ this.discoveredP2pIp = ip;
671
+ };
672
+ this.peerDiscoveryService.on('ip:changed', this.ipChangedHandler);
673
+ }
674
+
562
675
  this.discoveryRunningPromise = new RunningPromise(
563
676
  async () => {
564
677
  await this.peerManager.heartbeat();
@@ -584,6 +697,11 @@ export class LibP2PService extends WithTracer implements P2PService {
584
697
  // Remove gossip sub listener
585
698
  this.node.services.pubsub.removeEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
586
699
 
700
+ if (this.ipChangedHandler) {
701
+ this.peerDiscoveryService.removeListener('ip:changed', this.ipChangedHandler);
702
+ this.ipChangedHandler = undefined;
703
+ }
704
+
587
705
  // Stop peer manager
588
706
  this.logger.debug('Stopping peer manager...');
589
707
  await this.peerManager.stop();
@@ -598,12 +716,8 @@ export class LibP2PService extends WithTracer implements P2PService {
598
716
  this.logger.info('LibP2P service stopped');
599
717
  }
600
718
 
601
- addReqRespSubProtocol(
602
- subProtocol: ReqRespSubProtocol,
603
- handler: ReqRespSubProtocolHandler,
604
- validator?: ReqRespSubProtocolValidators[ReqRespSubProtocol],
605
- ): Promise<void> {
606
- return this.reqresp.addSubProtocol(subProtocol, handler, validator);
719
+ addReqRespSubProtocol(subProtocol: ReqRespSubProtocol, handler: ReqRespSubProtocolHandler): Promise<void> {
720
+ return this.reqresp.addSubProtocol(subProtocol, handler);
607
721
  }
608
722
 
609
723
  public registerThisValidatorAddresses(address: EthAddress[]): void {
@@ -631,20 +745,6 @@ export class LibP2PService extends WithTracer implements P2PService {
631
745
  setImmediate(() => void safeJob());
632
746
  }
633
747
 
634
- /**
635
- * Send a batch of requests to peers, and return the responses
636
- * @param protocol - The request response protocol to use
637
- * @param requests - The requests to send to the peers
638
- * @returns The responses to the requests
639
- */
640
- sendBatchRequest<SubProtocol extends ReqRespSubProtocol>(
641
- protocol: SubProtocol,
642
- requests: InstanceType<SubProtocolMap[SubProtocol]['request']>[],
643
- pinnedPeerId: PeerId | undefined,
644
- ): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']>[]> {
645
- return this.reqresp.sendBatchRequest(protocol, requests, pinnedPeerId);
646
- }
647
-
648
748
  public sendRequestToPeer(
649
749
  peerId: PeerId,
650
750
  subProtocol: ReqRespSubProtocol,
@@ -666,8 +766,12 @@ export class LibP2PService extends WithTracer implements P2PService {
666
766
  this.blockReceivedCallback = callback;
667
767
  }
668
768
 
669
- public registerCheckpointReceivedCallback(callback: P2PCheckpointReceivedCallback) {
670
- this.checkpointReceivedCallback = callback;
769
+ public registerValidatorCheckpointReceivedCallback(callback: P2PCheckpointReceivedCallback) {
770
+ this.validatorCheckpointReceivedCallback = callback;
771
+ }
772
+
773
+ public registerAllNodesCheckpointReceivedCallback(callback: P2PCheckpointReceivedCallback) {
774
+ this.allNodesCheckpointReceivedCallback = callback;
671
775
  }
672
776
 
673
777
  /**
@@ -680,6 +784,13 @@ export class LibP2PService extends WithTracer implements P2PService {
680
784
  this.duplicateProposalCallback = callback;
681
785
  }
682
786
 
787
+ /**
788
+ * Registers a callback to be invoked when an oversized block proposal is stored as slashing evidence.
789
+ */
790
+ public registerOversizedProposalCallback(callback: P2POversizedProposalCallback): void {
791
+ this.oversizedProposalCallback = callback;
792
+ }
793
+
683
794
  /**
684
795
  * Registers a callback to be invoked when a duplicate attestation is detected.
685
796
  * A validator signing attestations for different proposals at the same slot.
@@ -689,6 +800,10 @@ export class LibP2PService extends WithTracer implements P2PService {
689
800
  this.duplicateAttestationCallback = callback;
690
801
  }
691
802
 
803
+ public registerCheckpointAttestationCallback(callback: P2PCheckpointAttestationCallback): void {
804
+ this.checkpointAttestationCallback = callback;
805
+ }
806
+
692
807
  /**
693
808
  * Subscribes to a topic.
694
809
  * @param topic - The topic to subscribe to.
@@ -817,6 +932,15 @@ export class LibP2PService extends WithTracer implements P2PService {
817
932
 
818
933
  // Process the message, optionally within a linked span for trace propagation
819
934
  const processMessage = async () => {
935
+ if (
936
+ this.config.skipIncomingProposals &&
937
+ (msg.topic === this.topicStrings[TopicType.block_proposal] ||
938
+ msg.topic === this.topicStrings[TopicType.checkpoint_proposal])
939
+ ) {
940
+ this.logger.warn(`Ignoring incoming proposal (skipIncomingProposals is set)`, { topic: msg.topic });
941
+ this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Ignore);
942
+ return;
943
+ }
820
944
  if (msg.topic === this.topicStrings[TopicType.tx]) {
821
945
  await this.handleGossipedTx(p2pMessage.payload, msgId, source);
822
946
  } else if (msg.topic === this.topicStrings[TopicType.checkpoint_attestation]) {
@@ -882,30 +1006,67 @@ export class LibP2PService extends WithTracer implements P2PService {
882
1006
  source: PeerId,
883
1007
  topicType: TopicType,
884
1008
  ): Promise<ReceivedMessageValidationResult<T, M>> {
885
- let resultAndObj: ReceivedMessageValidationResult<T, M> = { result: TopicValidatorResult.Reject };
1009
+ // Default to reject result with a penalty if validation function throws an error
1010
+ let resultAndObj: ReceivedMessageValidationResult<T, M> = {
1011
+ result: TopicValidatorResult.Reject,
1012
+ severity: PeerErrorSeverity.MidToleranceError,
1013
+ };
886
1014
  const timer = new Timer();
887
1015
  try {
888
1016
  resultAndObj = await validationFunc();
889
1017
  } catch (err) {
890
- this.peerManager.penalizePeer(source, PeerErrorSeverity.LowToleranceError);
891
- this.logger.error(`Error deserializing and validating gossipsub message`, err, {
892
- msgId,
893
- source: source.toString(),
894
- topicType,
895
- });
1018
+ this.logger.error(`Error validating gossipsub message`, err, { msgId, source: source.toString(), topicType });
1019
+ }
1020
+
1021
+ const validationTimeMs = timer.ms();
1022
+ const mcacheWindowMs = this.config.gossipsubMcacheLength * this.config.gossipsubInterval;
1023
+ if (validationTimeMs > mcacheWindowMs * 0.75) {
1024
+ this.instrumentation.incSlowValidation(topicType);
1025
+ this.logger.warn(
1026
+ `Gossip validation for ${topicType} took ${validationTimeMs}ms, approaching mcache eviction window of ${mcacheWindowMs}ms. ` +
1027
+ `Message forwarding may be skipped if validation exceeds the window.`,
1028
+ { msgId, source: source.toString(), topicType, validationTimeMs, mcacheWindowMs },
1029
+ );
896
1030
  }
897
1031
 
898
1032
  if (resultAndObj.result === TopicValidatorResult.Accept) {
1033
+ this.logger.debug(`Message ${topicType} accepted by validator`, { msgId, source: source.toString(), topicType });
899
1034
  this.instrumentation.recordMessageValidation(topicType, timer);
1035
+ } else if (resultAndObj.result === TopicValidatorResult.Reject) {
1036
+ this.logger.warn(`Message ${topicType} rejected by validator with severity ${resultAndObj.severity}`, {
1037
+ msgId,
1038
+ source: source.toString(),
1039
+ topicType,
1040
+ severity: resultAndObj.severity,
1041
+ });
1042
+ this.peerManager.penalizePeer(source, resultAndObj.severity);
1043
+ } else {
1044
+ this.logger.trace(`Message ${topicType} ignored by validator`, { msgId, source: source.toString(), topicType });
900
1045
  }
901
1046
 
902
1047
  this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), resultAndObj.result);
903
1048
  return resultAndObj;
904
1049
  }
905
1050
 
1051
+ private tryDeserialize<T>(deserializeFunc: () => T, msgId: string, source: PeerId): T | undefined {
1052
+ try {
1053
+ return deserializeFunc();
1054
+ } catch (err) {
1055
+ this.logger.warn(`Failed to deserialize gossipsub message from buffer`, {
1056
+ err,
1057
+ msgId,
1058
+ source: source.toString(),
1059
+ });
1060
+ return undefined;
1061
+ }
1062
+ }
1063
+
906
1064
  protected async handleGossipedTx(payloadData: Buffer, msgId: string, source: PeerId) {
907
1065
  const validationFunc: () => Promise<ReceivedMessageValidationResult<Tx>> = async () => {
908
- const tx = Tx.fromBuffer(payloadData);
1066
+ const tx = this.tryDeserialize(() => Tx.fromBuffer(payloadData), msgId, source);
1067
+ if (!tx) {
1068
+ return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.LowToleranceError };
1069
+ }
909
1070
 
910
1071
  const currentBlockNumber = await this.archiver.getBlockNumber();
911
1072
  const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
@@ -930,8 +1091,7 @@ export class LibP2PService extends WithTracer implements P2PService {
930
1091
  severity,
931
1092
  source: source.toString(),
932
1093
  });
933
- this.peerManager.penalizePeer(source, severity);
934
- return { result: TopicValidatorResult.Reject };
1094
+ return { result: TopicValidatorResult.Reject, severity };
935
1095
  }
936
1096
 
937
1097
  // Pool pre-check: see if the pool would accept this tx before doing expensive proof verification
@@ -953,8 +1113,7 @@ export class LibP2PService extends WithTracer implements P2PService {
953
1113
  severity,
954
1114
  source: source.toString(),
955
1115
  });
956
- this.peerManager.penalizePeer(source, severity);
957
- return { result: TopicValidatorResult.Reject };
1116
+ return { result: TopicValidatorResult.Reject, severity };
958
1117
  }
959
1118
 
960
1119
  // Pool add: persist the tx
@@ -979,8 +1138,7 @@ export class LibP2PService extends WithTracer implements P2PService {
979
1138
  source: source.toString(),
980
1139
  txHash: txHash.toString(),
981
1140
  });
982
- this.peerManager.penalizePeer(source, PeerErrorSeverity.HighToleranceError);
983
- return { result: TopicValidatorResult.Reject };
1141
+ return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.HighToleranceError };
984
1142
  }
985
1143
  };
986
1144
 
@@ -1010,7 +1168,16 @@ export class LibP2PService extends WithTracer implements P2PService {
1010
1168
  source: PeerId,
1011
1169
  ): Promise<void> {
1012
1170
  const { result, obj: attestation } = await this.validateReceivedMessage<CheckpointAttestation>(
1013
- () => this.validateAndStoreCheckpointAttestation(source, CheckpointAttestation.fromBuffer(payloadData)),
1171
+ () => {
1172
+ const attestation = this.tryDeserialize(() => CheckpointAttestation.fromBuffer(payloadData), msgId, source);
1173
+ if (!attestation) {
1174
+ return Promise.resolve({
1175
+ result: TopicValidatorResult.Reject,
1176
+ severity: PeerErrorSeverity.LowToleranceError,
1177
+ });
1178
+ }
1179
+ return this.validateAndStoreCheckpointAttestation(source, attestation);
1180
+ },
1014
1181
  msgId,
1015
1182
  source,
1016
1183
  TopicType.checkpoint_attestation,
@@ -1043,8 +1210,7 @@ export class LibP2PService extends WithTracer implements P2PService {
1043
1210
 
1044
1211
  if (validationResult.result === 'reject') {
1045
1212
  this.logger.warn(`Penalizing peer ${peerId} for checkpoint attestation validation failure`);
1046
- this.peerManager.penalizePeer(peerId, validationResult.severity);
1047
- return { result: TopicValidatorResult.Reject };
1213
+ return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
1048
1214
  }
1049
1215
 
1050
1216
  if (validationResult.result === 'ignore') {
@@ -1070,16 +1236,16 @@ export class LibP2PService extends WithTracer implements P2PService {
1070
1236
  return { result: TopicValidatorResult.Ignore, obj: attestation };
1071
1237
  }
1072
1238
 
1073
- // Could not add (cap reached for signer), no need to re-broadcast
1239
+ // Could not add (cap reached for signer), penalize and do not re-broadcast
1074
1240
  if (!added) {
1075
- this.logger.warn(`Dropping checkpoint attestation due to cap`, {
1241
+ this.logger.warn(`Rejecting checkpoint attestation due to cap`, {
1076
1242
  slot: slot.toString(),
1077
1243
  archive: attestation.archive.toString(),
1078
1244
  source: peerId.toString(),
1079
1245
  attester: attestation.getSender()?.toString(),
1080
1246
  count,
1081
1247
  });
1082
- return { result: TopicValidatorResult.Ignore, obj: attestation };
1248
+ return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.HighToleranceError };
1083
1249
  }
1084
1250
 
1085
1251
  // Check if this is a duplicate attestation (signer attested to a different proposal at the same slot)
@@ -1098,6 +1264,7 @@ export class LibP2PService extends WithTracer implements P2PService {
1098
1264
  }
1099
1265
 
1100
1266
  // Attestation was added successfully - accept it so other nodes can also detect the equivocation
1267
+ this.checkpointAttestationCallback?.(attestation);
1101
1268
  return { result: TopicValidatorResult.Accept, obj: attestation };
1102
1269
  }
1103
1270
 
@@ -1105,16 +1272,17 @@ export class LibP2PService extends WithTracer implements P2PService {
1105
1272
  const {
1106
1273
  result,
1107
1274
  obj: block,
1108
- metadata: { isEquivocated } = {},
1109
- } = await this.validateReceivedMessage<BlockProposal, { isEquivocated: boolean }>(
1275
+ metadata: { isEquivocated, isOversized } = {},
1276
+ } = await this.validateReceivedMessage<BlockProposal, { isEquivocated: boolean; isOversized: boolean }>(
1110
1277
  () => this.validateAndStoreBlockProposal(source, BlockProposal.fromBuffer(payloadData)),
1111
1278
  msgId,
1112
1279
  source,
1113
1280
  TopicType.block_proposal,
1114
1281
  );
1115
1282
 
1116
- // If not accepted or equivocated, return
1117
- if (result !== TopicValidatorResult.Accept || !block || isEquivocated) {
1283
+ // If not accepted, equivocated, or oversized, return. Oversized proposals are re-broadcast as
1284
+ // slashing evidence but never attested or processed.
1285
+ if (result !== TopicValidatorResult.Accept || !block || isEquivocated || isOversized) {
1118
1286
  return;
1119
1287
  }
1120
1288
 
@@ -1129,13 +1297,12 @@ export class LibP2PService extends WithTracer implements P2PService {
1129
1297
  protected async validateAndStoreBlockProposal(
1130
1298
  peerId: PeerId,
1131
1299
  block: BlockProposal,
1132
- ): Promise<ReceivedMessageValidationResult<BlockProposal, { isEquivocated: boolean }>> {
1300
+ ): Promise<ReceivedMessageValidationResult<BlockProposal, { isEquivocated: boolean; isOversized: boolean }>> {
1133
1301
  const validationResult = await this.blockProposalValidator.validate(block);
1134
1302
 
1135
1303
  if (validationResult.result === 'reject') {
1136
1304
  this.logger.warn(`Penalizing peer ${peerId} for block proposal validation failure`);
1137
- this.peerManager.penalizePeer(peerId, validationResult.severity);
1138
- return { result: TopicValidatorResult.Reject };
1305
+ return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
1139
1306
  }
1140
1307
 
1141
1308
  if (validationResult.result === 'ignore') {
@@ -1145,6 +1312,12 @@ export class LibP2PService extends WithTracer implements P2PService {
1145
1312
  // Try to add the proposal: this handles existence check, cap check, and adding in one call
1146
1313
  const { added, alreadyExists, count } = await this.mempools.attestationPool.tryAddBlockProposal(block);
1147
1314
  const isEquivocated = count !== undefined && count > 1;
1315
+ // An oversized proposal (index at or beyond the consensus per-checkpoint limit) is structurally valid
1316
+ // proposer misbehavior: it is stored and re-broadcast as slashing evidence but never processed or
1317
+ // attested to. No-ops when maxBlocksPerCheckpoint is unset (local/test).
1318
+ const isOversized =
1319
+ this.config.maxBlocksPerCheckpoint !== undefined &&
1320
+ block.indexWithinCheckpoint >= this.config.maxBlocksPerCheckpoint;
1148
1321
 
1149
1322
  // Duplicate proposal received, no need to re-broadcast
1150
1323
  if (alreadyExists) {
@@ -1154,12 +1327,11 @@ export class LibP2PService extends WithTracer implements P2PService {
1154
1327
  proposer: block.getSender()?.toString(),
1155
1328
  source: peerId.toString(),
1156
1329
  });
1157
- return { result: TopicValidatorResult.Ignore, obj: block, metadata: { isEquivocated } };
1330
+ return { result: TopicValidatorResult.Ignore, obj: block, metadata: { isEquivocated, isOversized } };
1158
1331
  }
1159
1332
 
1160
1333
  // Too many blocks received for this slot and index, penalize peer and do not re-broadcast
1161
1334
  if (!added) {
1162
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.HighToleranceError);
1163
1335
  this.logger.warn(`Penalizing peer for block proposal exceeding per-position cap`, {
1164
1336
  ...block.toBlockInfo(),
1165
1337
  indexWithinCheckpoint: block.indexWithinCheckpoint,
@@ -1167,7 +1339,27 @@ export class LibP2PService extends WithTracer implements P2PService {
1167
1339
  proposer: block.getSender()?.toString(),
1168
1340
  source: peerId.toString(),
1169
1341
  });
1170
- return { result: TopicValidatorResult.Reject, metadata: { isEquivocated } };
1342
+ return {
1343
+ result: TopicValidatorResult.Reject,
1344
+ metadata: { isEquivocated, isOversized },
1345
+ severity: PeerErrorSeverity.HighToleranceError,
1346
+ };
1347
+ }
1348
+
1349
+ // The proposal was stored: if oversized, invoke the oversized callback so the proposer can be
1350
+ // slashed. Fired alongside (not instead of) equivocation detection below.
1351
+ if (isOversized) {
1352
+ const proposer = block.getSender();
1353
+ if (proposer) {
1354
+ this.logger.warn(`Detected oversized block proposal at slot ${block.slotNumber}`, {
1355
+ ...block.toBlockInfo(),
1356
+ indexWithinCheckpoint: block.indexWithinCheckpoint,
1357
+ maxBlocksPerCheckpoint: this.config.maxBlocksPerCheckpoint,
1358
+ source: peerId.toString(),
1359
+ proposer: proposer.toString(),
1360
+ });
1361
+ this.oversizedProposalCallback?.({ slot: block.slotNumber, proposer });
1362
+ }
1171
1363
  }
1172
1364
 
1173
1365
  // If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
@@ -1183,11 +1375,11 @@ export class LibP2PService extends WithTracer implements P2PService {
1183
1375
  if (proposer && count === 2) {
1184
1376
  this.duplicateProposalCallback?.({ slot: block.slotNumber, proposer, type: 'block' });
1185
1377
  }
1186
- return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated } };
1378
+ return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated, isOversized } };
1187
1379
  }
1188
1380
 
1189
1381
  // Otherwise, we're good to go!
1190
- return { result: TopicValidatorResult.Accept, obj: block };
1382
+ return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated: false, isOversized } };
1191
1383
  }
1192
1384
 
1193
1385
  // REFACTOR(palla): This method should be moved to the p2p_client or to a separate component,
@@ -1213,6 +1405,9 @@ export class LibP2PService extends WithTracer implements P2PService {
1213
1405
  const isValid = await this.blockReceivedCallback(block, sender);
1214
1406
  if (!isValid) {
1215
1407
  this.logger.info(`Block proposal validation failed for block ${block.blockNumber}`, block.toBlockInfo());
1408
+ // Release the protections this proposal created so its txs return to pending. Only entries still
1409
+ // keyed to this slot are cleared, so a tx referenced by a live proposal at another slot stays protected.
1410
+ await this.mempools.txPool.unprotectTxs(block.txHashes, slot);
1216
1411
  }
1217
1412
  }
1218
1413
 
@@ -1224,25 +1419,45 @@ export class LibP2PService extends WithTracer implements P2PService {
1224
1419
  const {
1225
1420
  result,
1226
1421
  obj: checkpoint,
1227
- metadata: { isEquivocated, processBlock } = {},
1228
- } = await this.validateReceivedMessage<CheckpointProposal, { isEquivocated: boolean; processBlock: boolean }>(
1422
+ metadata: { isEquivocated, processBlock, isOversized } = {},
1423
+ } = await this.validateReceivedMessage<
1424
+ CheckpointProposal,
1425
+ { isEquivocated: boolean; processBlock: boolean; isOversized: boolean }
1426
+ >(
1229
1427
  () => this.validateAndStoreCheckpointProposal(source, CheckpointProposal.fromBuffer(payloadData)),
1230
1428
  msgId,
1231
1429
  source,
1232
1430
  TopicType.checkpoint_proposal,
1233
1431
  );
1234
1432
 
1433
+ // An oversized checkpoint is re-broadcast as slashing evidence but never attested or processed.
1434
+ // Process checkpoint proposal if valid and neither equivocated nor oversized.
1435
+ const processCheckpointFn = () =>
1436
+ result === TopicValidatorResult.Accept && checkpoint && !isEquivocated && !isOversized
1437
+ ? this.processValidCheckpointProposal(checkpoint.toCore(), source)
1438
+ : Promise.resolve();
1439
+
1235
1440
  // If the checkpoint contained a valid last block, we process it even if the checkpoint itself is to be rejected
1236
1441
  // TODO(palla/mbps): Is this ok? Should we be considering a block from a checkpoint that was equivocated?
1237
- if (processBlock && checkpoint?.getBlockProposal()) {
1238
- await this.processValidBlockProposal(checkpoint.getBlockProposal()!, source);
1239
- }
1240
-
1241
- if (result !== TopicValidatorResult.Accept || !checkpoint || isEquivocated) {
1442
+ const processBlockFn = () =>
1443
+ processBlock && checkpoint && checkpoint.getBlockProposal()
1444
+ ? this.processValidBlockProposal(checkpoint.getBlockProposal()!, source)
1445
+ : Promise.resolve();
1446
+
1447
+ // A node that skips checkpoint validation attests without re-executing the embedded last block, so run
1448
+ // the checkpoint callback first: this creates and broadcasts the attestation before the block is
1449
+ // processed. Otherwise the block's re-execution — which can stall until the re-execution deadline
1450
+ // waiting for a parent that may never arrive — would delay the attestation past the slot's attestation
1451
+ // window, after which peers reject it as stale.
1452
+ if (this.config.skipCheckpointProposalValidation) {
1453
+ await processCheckpointFn();
1454
+ await processBlockFn();
1242
1455
  return;
1243
1456
  }
1244
1457
 
1245
- await this.processValidCheckpointProposal(checkpoint.toCore(), source);
1458
+ // Process the block first, since it's required for the checkpoint proposal validation.
1459
+ await processBlockFn();
1460
+ await processCheckpointFn();
1246
1461
  }
1247
1462
 
1248
1463
  /**
@@ -1255,13 +1470,17 @@ export class LibP2PService extends WithTracer implements P2PService {
1255
1470
  protected async validateAndStoreCheckpointProposal(
1256
1471
  peerId: PeerId,
1257
1472
  checkpoint: CheckpointProposal,
1258
- ): Promise<ReceivedMessageValidationResult<CheckpointProposal, { isEquivocated: boolean; processBlock: boolean }>> {
1473
+ ): Promise<
1474
+ ReceivedMessageValidationResult<
1475
+ CheckpointProposal,
1476
+ { isEquivocated: boolean; processBlock: boolean; isOversized: boolean }
1477
+ >
1478
+ > {
1259
1479
  const validationResult = await this.checkpointProposalValidator.validate(checkpoint);
1260
1480
 
1261
1481
  if (validationResult.result === 'reject') {
1262
1482
  this.logger.warn(`Penalizing peer ${peerId} for checkpoint proposal validation failure`);
1263
- this.peerManager.penalizePeer(peerId, validationResult.severity);
1264
- return { result: TopicValidatorResult.Reject };
1483
+ return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
1265
1484
  }
1266
1485
 
1267
1486
  if (validationResult.result === 'ignore') {
@@ -1271,25 +1490,30 @@ export class LibP2PService extends WithTracer implements P2PService {
1271
1490
  // Extract and try to add the block proposal first if present
1272
1491
  const blockProposal = checkpoint.getBlockProposal();
1273
1492
  let processBlock = false;
1493
+ let isOversized = false;
1274
1494
  if (blockProposal) {
1275
1495
  this.logger.debug(`Validating block proposal from propagated checkpoint`, {
1276
1496
  [Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
1277
1497
  [Attributes.P2P_ID]: peerId.toString(),
1278
1498
  });
1279
- const {
1280
- result,
1281
- obj,
1282
- metadata: { isEquivocated } = {},
1283
- } = await this.validateAndStoreBlockProposal(peerId, blockProposal);
1284
- if (result === TopicValidatorResult.Reject || !obj || isEquivocated) {
1499
+ const blockProposalResult = await this.validateAndStoreBlockProposal(peerId, blockProposal);
1500
+ const { obj, metadata: { isEquivocated, isOversized: blockIsOversized } = {} } = blockProposalResult;
1501
+ isOversized = blockIsOversized ?? false;
1502
+
1503
+ if (blockProposalResult.result === TopicValidatorResult.Reject || !obj || isEquivocated) {
1285
1504
  this.logger.debug(`Rejecting checkpoint due to invalid last block proposal`, {
1286
1505
  [Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
1287
1506
  [Attributes.P2P_ID]: peerId.toString(),
1288
1507
  isEquivocated,
1289
- result,
1508
+ result: blockProposalResult.result,
1290
1509
  });
1291
- return { result: TopicValidatorResult.Reject };
1292
- } else if (result === TopicValidatorResult.Accept && obj && !isEquivocated) {
1510
+ return {
1511
+ result: TopicValidatorResult.Reject,
1512
+ severity:
1513
+ 'severity' in blockProposalResult ? blockProposalResult.severity : PeerErrorSeverity.MidToleranceError,
1514
+ };
1515
+ } else if (blockProposalResult.result === TopicValidatorResult.Accept && obj && !isEquivocated && !isOversized) {
1516
+ // An oversized terminal block is re-broadcast as slashing evidence but never processed.
1293
1517
  processBlock = true;
1294
1518
  }
1295
1519
  }
@@ -1309,20 +1533,24 @@ export class LibP2PService extends WithTracer implements P2PService {
1309
1533
  return {
1310
1534
  result: TopicValidatorResult.Ignore,
1311
1535
  obj: checkpoint,
1312
- metadata: { isEquivocated, processBlock },
1536
+ metadata: { isEquivocated, processBlock, isOversized },
1313
1537
  };
1314
1538
  }
1315
1539
 
1316
- // Too many checkpoint proposals received for this slot, penalize peer and do not re-broadcast
1540
+ // Too many checkpoint proposals received for this slot, penalize peer and do not re-broadcast.
1317
1541
  // Note: We still return the checkpoint obj so the lastBlock can be processed if valid
1318
1542
  if (!added) {
1319
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.HighToleranceError);
1320
1543
  this.logger.warn(`Penalizing peer for checkpoint proposal exceeding per-slot cap`, {
1321
1544
  ...checkpoint.toCheckpointInfo(),
1322
1545
  count,
1323
1546
  source: peerId.toString(),
1324
1547
  });
1325
- return { result: TopicValidatorResult.Reject, obj: checkpoint, metadata: { isEquivocated, processBlock } };
1548
+ return {
1549
+ result: TopicValidatorResult.Reject,
1550
+ obj: checkpoint,
1551
+ metadata: { isEquivocated, processBlock, isOversized },
1552
+ severity: PeerErrorSeverity.HighToleranceError,
1553
+ };
1326
1554
  }
1327
1555
 
1328
1556
  // If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
@@ -1341,12 +1569,16 @@ export class LibP2PService extends WithTracer implements P2PService {
1341
1569
  return {
1342
1570
  result: TopicValidatorResult.Accept,
1343
1571
  obj: checkpoint,
1344
- metadata: { isEquivocated, processBlock },
1572
+ metadata: { isEquivocated, processBlock, isOversized },
1345
1573
  };
1346
1574
  }
1347
1575
 
1348
1576
  // Otherwise, we're good to go!
1349
- return { result: TopicValidatorResult.Accept, obj: checkpoint, metadata: { processBlock, isEquivocated } };
1577
+ return {
1578
+ result: TopicValidatorResult.Accept,
1579
+ obj: checkpoint,
1580
+ metadata: { processBlock, isEquivocated, isOversized },
1581
+ };
1350
1582
  }
1351
1583
 
1352
1584
  /**
@@ -1367,9 +1599,11 @@ export class LibP2PService extends WithTracer implements P2PService {
1367
1599
  source: sender.toString(),
1368
1600
  });
1369
1601
 
1602
+ await this.allNodesCheckpointReceivedCallback(checkpoint, sender);
1603
+
1370
1604
  // Call the checkpoint received callback with the core version (without lastBlock)
1371
1605
  // to validate and potentially generate attestations
1372
- const attestations = await this.checkpointReceivedCallback(checkpoint, sender);
1606
+ const attestations = await this.validatorCheckpointReceivedCallback(checkpoint, sender);
1373
1607
  if (attestations && attestations.length > 0) {
1374
1608
  // If the callback returned attestations, add them to the pool and propagate them
1375
1609
  await this.mempools.attestationPool.addOwnCheckpointAttestations(attestations);
@@ -1392,82 +1626,87 @@ export class LibP2PService extends WithTracer implements P2PService {
1392
1626
  }
1393
1627
 
1394
1628
  /**
1395
- * Validate the requested block transactions. Allow partial returns.
1629
+ * Validate the requested block transactions request-response consistency.
1630
+ * It does NOT validate the transactions themselves.
1396
1631
  * @param request - The block transactions request.
1397
1632
  * @param response - The block transactions response.
1398
1633
  * @param peerId - The ID of the peer that made the request.
1399
- * @returns True if the requested block transactions are valid, false otherwise.
1634
+ * @returns True if the request-response is consistent, false otherwise.
1400
1635
  */
1401
- @trackSpan('Libp2pService.validateRequestedBlockTxs', request => ({
1636
+ @trackSpan('Libp2pService.validateRequestedBlockTxsConsistency', request => ({
1402
1637
  [Attributes.BLOCK_ARCHIVE]: request.archiveRoot.toString(),
1403
1638
  }))
1404
- protected async validateRequestedBlockTxs(
1639
+ protected async validateRequestedBlockTxsConsistency(
1405
1640
  request: BlockTxsRequest,
1406
1641
  response: BlockTxsResponse,
1407
1642
  peerId: PeerId,
1408
1643
  ): Promise<boolean> {
1409
- const requestedTxValidator = this.createRequestedTxValidator();
1410
-
1411
1644
  try {
1412
- if (!response.archiveRoot.equals(request.archiveRoot)) {
1645
+ // Check for duplicates txs (or hashes) in the response.
1646
+ const returnedHashes = response.txs.map(tx => tx.getTxHash().toString());
1647
+ const uniqueReturned = new Set(returnedHashes.map(h => h.toString()));
1648
+ if (uniqueReturned.size !== returnedHashes.length) {
1413
1649
  this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1414
- throw new ValidationError(
1415
- `Received block txs for unexpected archive root: expected ${request.archiveRoot.toString()}, got ${response.archiveRoot.toString()}`,
1650
+ throw new ValidationError(`Received duplicate txs in block txs response`);
1651
+ }
1652
+
1653
+ // We get the block tx hashes from the proposal or the archiver.
1654
+ const proposal = await this.mempools.attestationPool.getBlockProposalByArchive(request.archiveRoot.toString());
1655
+ const blockTxHashes =
1656
+ proposal?.txHashes ??
1657
+ (await this.archiver.getBlock({ archive: request.archiveRoot }))?.body.txEffects.map(e => e.txHash);
1658
+
1659
+ // If we don't have the block, we can't verify membership/order of the returned txs.
1660
+ if (!blockTxHashes) {
1661
+ // This shouldn't happen, since we asked for the block (proposal).
1662
+ // Neither a local proposal nor an archived block: we cannot verify membership/order of the
1663
+ // returned txs. This is a local-state gap, not a peer fault, so we do not penalize.
1664
+ this.logger.warn(
1665
+ `Block ${request.archiveRoot.toString()} not found in attestation pool or archiver; cannot validate membership/order of returned txs`,
1416
1666
  );
1667
+ // NOTE: We mark the response as invalid, given the limitations of a true/false return value.
1668
+ return false;
1417
1669
  }
1418
1670
 
1419
- if (response.txIndices.getLength() !== request.txIndices.getLength()) {
1420
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1671
+ // Verify that the returned tx hashes are a subset of (tx hashes by index) U (explicitly requested tx hashes).
1672
+ const uniqueRequestedHashes = new Set([
1673
+ // tx hashes requested by index.
1674
+ ...request.txIndices.getTrueIndices().map(i => blockTxHashes[i].toString()),
1675
+ // explicitly requested tx hashes.
1676
+ ...request.txHashes.map(h => h.toString()),
1677
+ ]);
1678
+ if (!returnedHashes.every(h => uniqueRequestedHashes.has(h))) {
1679
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1421
1680
  throw new ValidationError(
1422
- `Received block txs with mismatched bitvector length: expected ${request.txIndices.getLength()}, got ${response.txIndices.getLength()}`,
1681
+ 'Returned txs should be a subset of (tx hashes by index) U (explicitly requested tx hashes)',
1423
1682
  );
1424
1683
  }
1425
1684
 
1426
- // Check no duplicates and not exceeding returnable count
1427
- const requestedIndices = new Set(request.txIndices.getTrueIndices());
1428
- const availableIndices = new Set(response.txIndices.getTrueIndices());
1429
- const maxReturnable = [...requestedIndices].filter(i => availableIndices.has(i)).length;
1430
-
1431
- const returnedHashes = await Promise.all(response.txs.map(tx => tx.getTxHash().toString()));
1432
- const uniqueReturned = new Set(returnedHashes.map(h => h.toString()));
1433
- if (uniqueReturned.size !== returnedHashes.length) {
1434
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1435
- throw new ValidationError(`Received duplicate txs in block txs response`);
1685
+ // Tx indices information is optional, but if present, we need to verify a few things.
1686
+ if (!response.peerHasBlock()) {
1687
+ this.logger.debug(`Peer ${peerId.toString()} signalled missing block`);
1688
+ return true;
1436
1689
  }
1437
- if (response.txs.length > maxReturnable) {
1690
+
1691
+ // If the response has a non-empty bitvector, it needs to be consistent with the number of txs in the block.
1692
+ if (response.txIndices.getLength() !== blockTxHashes.length) {
1438
1693
  this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1439
1694
  throw new ValidationError(
1440
- `Received more txs (${response.txs.length}) than requested-and-available (${maxReturnable})`,
1695
+ `Received block txs with mismatched bitvector length: expected ${blockTxHashes.length}, got ${response.txIndices.getLength()}`,
1441
1696
  );
1442
1697
  }
1443
1698
 
1444
- // Given proposal (should have locally), ensure returned txs are valid subset and match request indices
1445
- const proposal = await this.mempools.attestationPool.getBlockProposal(request.archiveRoot.toString());
1446
- if (proposal) {
1447
- // Build intersected indices
1448
- const intersectIdx = request.txIndices.getTrueIndices().filter(i => response.txIndices.isSet(i));
1449
-
1450
- // Enforce subset membership and preserve increasing order by index.
1451
- const hashToIndexInProposal = new Map<string, number>(
1452
- proposal.txHashes.map((h, i) => [h.toString(), i] as [string, number]),
1453
- );
1454
- const allowedIndexSet = new Set(intersectIdx);
1455
- const indices = returnedHashes.map(h => hashToIndexInProposal.get(h));
1456
- const allAllowed = indices.every(idx => idx !== undefined && allowedIndexSet.has(idx));
1457
- const strictlyIncreasing = indices.every((idx, i) => (i === 0 ? idx !== undefined : idx! > indices[i - 1]!));
1458
- if (!allAllowed || !strictlyIncreasing) {
1459
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1460
- throw new ValidationError('Returned txs do not match expected subset/order for requested indices');
1461
- }
1462
- } else {
1463
- // No local proposal, cannot check the membership/order of the returned txs
1464
- this.logger.warn(
1465
- `Block proposal not found for archive root ${request.archiveRoot.toString()}; cannot validate membership/order of returned txs`,
1466
- );
1467
- return false;
1699
+ // For every index we requested that the peer claims to have (per its bitvector), the
1700
+ // corresponding tx must be present in the response. A peer advertising availability but
1701
+ // withholding the tx is inconsistent.
1702
+ const returnedHashSet = new Set(returnedHashes);
1703
+ const expectedIndices = request.txIndices.getTrueIndices().filter(i => response.txIndices.isSet(i));
1704
+ const missingExpected = expectedIndices.some(i => !returnedHashSet.has(blockTxHashes[i].toString()));
1705
+ if (missingExpected) {
1706
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1707
+ throw new ValidationError('Peer advertised requested txs via indices but did not return them');
1468
1708
  }
1469
1709
 
1470
- await Promise.all(response.txs.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator)));
1471
1710
  return true;
1472
1711
  } catch (e: any) {
1473
1712
  if (e instanceof ValidationError) {
@@ -1480,125 +1719,8 @@ export class LibP2PService extends WithTracer implements P2PService {
1480
1719
  }
1481
1720
  }
1482
1721
 
1483
- /**
1484
- * Validate a collection of txs that has been requested from a peer.
1485
- *
1486
- * The core component of this validator is that each tx hash MUST match the requested tx hash,
1487
- * In order to perform this check, the tx proof must be verified.
1488
- *
1489
- * Note: This function is called from within `ReqResp.sendRequest` as part of the
1490
- * ReqRespSubProtocol.TX subprotocol validation.
1491
- *
1492
- * @param requestedTxHash - The collection of the txs that was requested.
1493
- * @param responseTx - The collection of txs that was received as a response to the request.
1494
- * @param peerId - The peer ID of the peer that sent the tx.
1495
- * @returns True if the whole collection of txs is valid, false otherwise.
1496
- */
1497
- @trackSpan('Libp2pService.validateRequestedTx', (requestedTxHash, _responseTx) => ({
1498
- [Attributes.TX_HASH]: requestedTxHash.toString(),
1499
- }))
1500
- private async validateRequestedTxs(requestedTxHash: TxHash[], responseTx: Tx[], peerId: PeerId): Promise<boolean> {
1501
- const requested = new Set(requestedTxHash.map(h => h.toString()));
1502
- const requestedTxValidator = this.createRequestedTxValidator();
1503
-
1504
- //TODO: (mralj) - this is somewhat naive implementation, if single tx is invalid we consider the whole response invalid.
1505
- // I think we should still extract the valid txs and return them, so that we can still use the response.
1506
- try {
1507
- await Promise.all(responseTx.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator, requested)));
1508
- return true;
1509
- } catch (e: any) {
1510
- if (e instanceof ValidationError) {
1511
- this.logger.warn(`Failed to validate requested txs from peer ${peerId.toString()}, reason ${e.message}`);
1512
- } else {
1513
- this.logger.error(`Error during validation of requested txs`, e);
1514
- }
1515
-
1516
- return false;
1517
- }
1518
- }
1519
-
1520
- /**
1521
- * Validates a BLOCK response.
1522
- *
1523
- * If a local copy exists, enforces hash equality. If missing, rejects (no penalty) since the hash cannot be verified.
1524
- * Penalizes on block number mismatch or hash mismatch.
1525
- *
1526
- * @param requestedBlockNumber - The requested block number.
1527
- * @param responseBlock - The block returned by the peer.
1528
- * @param peerId - The peer that returned the block.
1529
- * @returns True if the response is valid, false otherwise.
1530
- */
1531
- @trackSpan('Libp2pService.validateRequestedBlock', (requestedBlockNumber, _responseBlock) => ({
1532
- [Attributes.BLOCK_NUMBER]: requestedBlockNumber.toString(),
1533
- }))
1534
- protected async validateRequestedBlock(
1535
- requestedBlockNumber: Fr,
1536
- responseBlock: L2Block,
1537
- peerId: PeerId,
1538
- ): Promise<boolean> {
1539
- try {
1540
- const reqNum = Number(requestedBlockNumber.toString());
1541
- if (responseBlock.number !== reqNum) {
1542
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1543
- return false;
1544
- }
1545
-
1546
- const local = await this.archiver.getBlock(BlockNumber(reqNum));
1547
- if (!local) {
1548
- // We are missing the local block; we cannot verify the hash yet. Reject without penalizing.
1549
- // TODO: Consider extending this validator to accept an expected hash or
1550
- // performing quorum-based checks when using P2P syncing prior to L1 sync.
1551
- this.logger.warn(`Local block ${reqNum} not found; rejecting BLOCK response without hash verification`);
1552
- return false;
1553
- }
1554
- const [localHash, respHash] = await Promise.all([local.hash(), responseBlock.hash()]);
1555
- if (!localHash.equals(respHash)) {
1556
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1557
- return false;
1558
- }
1559
-
1560
- return true;
1561
- } catch (e) {
1562
- this.logger.warn(`Error validating requested block`, e);
1563
- return false;
1564
- }
1565
- }
1566
-
1567
- protected async validateRequestedTx(
1568
- tx: Tx,
1569
- peerId: PeerId,
1570
- txValidator: TxValidator,
1571
- requested?: Set<`0x${string}`>,
1572
- ) {
1573
- const penalize = (severity: PeerErrorSeverity) => this.peerManager.penalizePeer(peerId, severity);
1574
- if (requested && !requested.has(tx.getTxHash().toString())) {
1575
- penalize(PeerErrorSeverity.MidToleranceError);
1576
- throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that was not requested.`);
1577
- }
1578
-
1579
- const { result } = await txValidator.validateTx(tx);
1580
- if (result === 'invalid') {
1581
- penalize(PeerErrorSeverity.LowToleranceError);
1582
- throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that is invalid.`);
1583
- }
1584
- }
1585
-
1586
- protected createRequestedTxValidator(): TxValidator {
1587
- return createTxValidatorForReqResponseReceivedTxs(this.proofVerifier, {
1588
- l1ChainId: this.config.l1ChainId,
1589
- rollupVersion: this.config.rollupVersion,
1590
- });
1591
- }
1592
-
1593
- private async getGasFees(blockNumber: BlockNumber): Promise<GasFees> {
1594
- if (blockNumber === this.feesCache?.blockNumber) {
1595
- return this.feesCache.gasFees;
1596
- }
1597
-
1598
- const header = await this.archiver.getBlockHeader(blockNumber);
1599
- const gasFees = header?.globalVariables.gasFees ?? GasFees.empty();
1600
- this.feesCache = { blockNumber, gasFees };
1601
- return gasFees;
1722
+ private getGasFees(): Promise<GasFees> {
1723
+ return this.blockMinFeesProvider.getCurrentMinFees();
1602
1724
  }
1603
1725
 
1604
1726
  /**
@@ -1612,8 +1734,10 @@ export class LibP2PService extends WithTracer implements P2PService {
1612
1734
  l1ChainId: this.config.l1ChainId,
1613
1735
  rollupVersion: this.config.rollupVersion,
1614
1736
  proofVerifier: this.proofVerifier,
1737
+ txValidationCache: this.txValidationCache,
1615
1738
  },
1616
1739
  peerScoring: this.peerManager,
1740
+ validateRequestedBlockTxsConsistency: this.validateRequestedBlockTxsConsistency.bind(this),
1617
1741
  };
1618
1742
  }
1619
1743
 
@@ -1622,6 +1746,7 @@ export class LibP2PService extends WithTracer implements P2PService {
1622
1746
  this.proofVerifier,
1623
1747
  { l1ChainId: this.config.l1ChainId, rollupVersion: this.config.rollupVersion },
1624
1748
  this.logger.getBindings(),
1749
+ this.txValidationCache,
1625
1750
  );
1626
1751
 
1627
1752
  const results = await Promise.all(
@@ -1640,13 +1765,14 @@ export class LibP2PService extends WithTracer implements P2PService {
1640
1765
  currentBlockNumber: BlockNumber,
1641
1766
  nextSlotTimestamp: UInt64,
1642
1767
  ): Promise<Record<string, TransactionValidator>> {
1643
- const gasFees = await this.getGasFees(currentBlockNumber);
1768
+ const gasFees = await this.getGasFees();
1644
1769
  const allowedInSetup = [
1645
1770
  ...(await getDefaultAllowedSetupFunctions()),
1646
1771
  ...(this.config.txPublicSetupAllowListExtend ?? []),
1647
1772
  ];
1648
1773
  const blockNumber = BlockNumber(currentBlockNumber + 1);
1649
1774
  const l1Constants = await this.archiver.getL1Constants();
1775
+ const networkTxGasLimits = getNetworkTxGasLimits(this.config, l1Constants);
1650
1776
 
1651
1777
  return createFirstStageTxValidationsForGossipedTransactions(
1652
1778
  nextSlotTimestamp,
@@ -1661,9 +1787,8 @@ export class LibP2PService extends WithTracer implements P2PService {
1661
1787
  allowedInSetup,
1662
1788
  this.logger.getBindings(),
1663
1789
  {
1664
- rollupManaLimit: l1Constants.rollupManaLimit,
1665
- maxBlockL2Gas: this.config.validateMaxL2BlockGas,
1666
- maxBlockDAGas: this.config.validateMaxDABlockGas,
1790
+ maxTxL2Gas: networkTxGasLimits.l2Gas,
1791
+ maxTxDAGas: networkTxGasLimits.daGas,
1667
1792
  },
1668
1793
  );
1669
1794
  }