@aztec/p2p 0.0.1-commit.4d79d1f2d → 0.0.1-commit.4d9804df

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