@aztec/p2p 0.0.1-commit.3fd054f6 → 0.0.1-commit.431c48d

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 (429) hide show
  1. package/README.md +7 -6
  2. package/dest/bootstrap/bootstrap.d.ts +1 -1
  3. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  4. package/dest/bootstrap/bootstrap.js +22 -6
  5. package/dest/client/factory.d.ts +4 -3
  6. package/dest/client/factory.d.ts.map +1 -1
  7. package/dest/client/factory.js +35 -25
  8. package/dest/client/interface.d.ts +52 -12
  9. package/dest/client/interface.d.ts.map +1 -1
  10. package/dest/client/p2p_client.d.ts +36 -20
  11. package/dest/client/p2p_client.d.ts.map +1 -1
  12. package/dest/client/p2p_client.js +127 -54
  13. package/dest/config.d.ts +150 -107
  14. package/dest/config.d.ts.map +1 -1
  15. package/dest/config.js +77 -26
  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/index.d.ts +1 -2
  23. package/dest/index.d.ts.map +1 -1
  24. package/dest/index.js +0 -1
  25. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +98 -56
  26. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  27. package/dest/mem_pools/attestation_pool/attestation_pool.js +269 -196
  28. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +1 -1
  29. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  30. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +218 -59
  31. package/dest/mem_pools/attestation_pool/mocks.d.ts +1 -1
  32. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  33. package/dest/mem_pools/attestation_pool/mocks.js +6 -4
  34. package/dest/mem_pools/index.d.ts +1 -2
  35. package/dest/mem_pools/index.d.ts.map +1 -1
  36. package/dest/mem_pools/instrumentation.d.ts +9 -4
  37. package/dest/mem_pools/instrumentation.d.ts.map +1 -1
  38. package/dest/mem_pools/instrumentation.js +28 -9
  39. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts +10 -5
  40. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts.map +1 -1
  41. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.js +29 -20
  42. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +3 -3
  43. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -1
  44. package/dest/mem_pools/tx_pool_v2/deleted_pool.js +6 -6
  45. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts +1 -1
  46. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -1
  47. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +3 -3
  48. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts +1 -1
  49. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts.map +1 -1
  50. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.js +3 -1
  51. package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts +2 -1
  52. package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts.map +1 -1
  53. package/dest/mem_pools/tx_pool_v2/eviction/index.js +1 -0
  54. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts +16 -0
  55. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts.map +1 -0
  56. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.js +64 -0
  57. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts +1 -1
  58. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts.map +1 -1
  59. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.js +3 -4
  60. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts +1 -1
  61. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts.map +1 -1
  62. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +6 -7
  63. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts +1 -1
  64. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts.map +1 -1
  65. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.js +6 -5
  66. package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts +11 -1
  67. package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts.map +1 -1
  68. package/dest/mem_pools/tx_pool_v2/instrumentation.js +24 -0
  69. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +33 -5
  70. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
  71. package/dest/mem_pools/tx_pool_v2/interfaces.js +2 -1
  72. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +5 -2
  73. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
  74. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +13 -6
  75. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +15 -1
  76. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -1
  77. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +31 -0
  78. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +16 -3
  79. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
  80. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +83 -32
  81. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +50 -6
  82. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
  83. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +243 -127
  84. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +10 -3
  85. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
  86. package/dest/msg_validators/attestation_validator/attestation_validator.js +38 -16
  87. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +7 -3
  88. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
  89. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +4 -5
  90. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +8 -3
  91. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
  92. package/dest/msg_validators/proposal_validator/block_proposal_validator.js +2 -2
  93. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +8 -3
  94. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
  95. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +8 -2
  96. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +25 -3
  97. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
  98. package/dest/msg_validators/proposal_validator/proposal_validator.js +85 -15
  99. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +1 -1
  100. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
  101. package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +8 -15
  102. package/dest/msg_validators/tx_validator/allowed_public_setup.js +3 -3
  103. package/dest/msg_validators/tx_validator/archive_cache.js +1 -1
  104. package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts +15 -0
  105. package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts.map +1 -0
  106. package/dest/msg_validators/tx_validator/cached_tx_validator.js +19 -0
  107. package/dest/msg_validators/tx_validator/data_validator.d.ts +2 -1
  108. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
  109. package/dest/msg_validators/tx_validator/data_validator.js +1 -0
  110. package/dest/msg_validators/tx_validator/factory.d.ts +14 -15
  111. package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
  112. package/dest/msg_validators/tx_validator/factory.js +29 -16
  113. package/dest/msg_validators/tx_validator/gas_validator.d.ts +47 -10
  114. package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
  115. package/dest/msg_validators/tx_validator/gas_validator.js +65 -48
  116. package/dest/msg_validators/tx_validator/index.d.ts +3 -1
  117. package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
  118. package/dest/msg_validators/tx_validator/index.js +2 -0
  119. package/dest/msg_validators/tx_validator/phases_validator.js +2 -2
  120. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +2 -1
  121. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -1
  122. package/dest/msg_validators/tx_validator/tx_proof_validator.js +2 -0
  123. package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts +48 -0
  124. package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts.map +1 -0
  125. package/dest/msg_validators/tx_validator/tx_validation_cache.js +68 -0
  126. package/dest/services/data_store.d.ts +1 -1
  127. package/dest/services/data_store.d.ts.map +1 -1
  128. package/dest/services/data_store.js +5 -5
  129. package/dest/services/discv5/discV5_service.d.ts +13 -2
  130. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  131. package/dest/services/discv5/discV5_service.js +119 -23
  132. package/dest/services/discv5/persisted_enr_store.d.ts +38 -0
  133. package/dest/services/discv5/persisted_enr_store.d.ts.map +1 -0
  134. package/dest/services/discv5/persisted_enr_store.js +145 -0
  135. package/dest/services/dummy_service.d.ts +18 -16
  136. package/dest/services/dummy_service.d.ts.map +1 -1
  137. package/dest/services/dummy_service.js +21 -16
  138. package/dest/services/encoding.d.ts +19 -6
  139. package/dest/services/encoding.d.ts.map +1 -1
  140. package/dest/services/encoding.js +25 -15
  141. package/dest/services/gossipsub/topic_score_params.d.ts +6 -12
  142. package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -1
  143. package/dest/services/gossipsub/topic_score_params.js +4 -14
  144. package/dest/services/libp2p/instrumentation.d.ts +6 -1
  145. package/dest/services/libp2p/instrumentation.d.ts.map +1 -1
  146. package/dest/services/libp2p/instrumentation.js +21 -0
  147. package/dest/services/libp2p/libp2p_service.d.ts +49 -44
  148. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  149. package/dest/services/libp2p/libp2p_service.js +388 -279
  150. package/dest/services/peer-manager/peer_manager.d.ts +31 -3
  151. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  152. package/dest/services/peer-manager/peer_manager.js +121 -23
  153. package/dest/services/peer-manager/peer_scoring.d.ts +34 -2
  154. package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
  155. package/dest/services/peer-manager/peer_scoring.js +102 -13
  156. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +1 -8
  157. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
  158. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +35 -44
  159. package/dest/services/reqresp/batch-tx-requester/interface.d.ts +8 -5
  160. package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -1
  161. package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts +5 -14
  162. package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts.map +1 -1
  163. package/dest/services/reqresp/batch-tx-requester/tx_validator.js +6 -20
  164. package/dest/services/reqresp/config.d.ts +15 -3
  165. package/dest/services/reqresp/config.d.ts.map +1 -1
  166. package/dest/services/reqresp/config.js +11 -0
  167. package/dest/services/reqresp/interface.d.ts +16 -18
  168. package/dest/services/reqresp/interface.d.ts.map +1 -1
  169. package/dest/services/reqresp/interface.js +10 -20
  170. package/dest/services/reqresp/metrics.d.ts +1 -1
  171. package/dest/services/reqresp/metrics.d.ts.map +1 -1
  172. package/dest/services/reqresp/metrics.js +0 -1
  173. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +4 -3
  174. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -1
  175. package/dest/services/reqresp/protocols/block_txs/bitvector.js +3 -2
  176. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +1 -1
  177. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
  178. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +31 -28
  179. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +12 -7
  180. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
  181. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +42 -24
  182. package/dest/services/reqresp/protocols/index.d.ts +1 -2
  183. package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
  184. package/dest/services/reqresp/protocols/index.js +0 -1
  185. package/dest/services/reqresp/protocols/tx.d.ts +1 -1
  186. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
  187. package/dest/services/reqresp/protocols/tx.js +1 -3
  188. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +5 -4
  189. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
  190. package/dest/services/reqresp/rate-limiter/rate_limiter.js +10 -8
  191. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
  192. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
  193. package/dest/services/reqresp/rate-limiter/rate_limits.js +3 -13
  194. package/dest/services/reqresp/reqresp.d.ts +7 -29
  195. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  196. package/dest/services/reqresp/reqresp.js +59 -209
  197. package/dest/services/service.d.ts +43 -18
  198. package/dest/services/service.d.ts.map +1 -1
  199. package/dest/services/tx_collection/config.d.ts +2 -23
  200. package/dest/services/tx_collection/config.d.ts.map +1 -1
  201. package/dest/services/tx_collection/config.js +2 -55
  202. package/dest/services/tx_collection/file_store_tx_collection.d.ts +12 -28
  203. package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -1
  204. package/dest/services/tx_collection/file_store_tx_collection.js +43 -83
  205. package/dest/services/tx_collection/file_store_tx_source.d.ts +5 -4
  206. package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -1
  207. package/dest/services/tx_collection/file_store_tx_source.js +39 -29
  208. package/dest/services/tx_collection/index.d.ts +2 -3
  209. package/dest/services/tx_collection/index.d.ts.map +1 -1
  210. package/dest/services/tx_collection/index.js +0 -1
  211. package/dest/services/tx_collection/instrumentation.d.ts +1 -1
  212. package/dest/services/tx_collection/instrumentation.d.ts.map +1 -1
  213. package/dest/services/tx_collection/instrumentation.js +0 -2
  214. package/dest/services/tx_collection/tx_collection.d.ts +35 -51
  215. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
  216. package/dest/services/tx_collection/tx_collection.js +275 -119
  217. package/dest/services/tx_collection/tx_collection_sink.d.ts +1 -1
  218. package/dest/services/tx_collection/tx_collection_sink.js +2 -2
  219. package/dest/services/tx_collection/tx_source.d.ts +6 -5
  220. package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
  221. package/dest/services/tx_collection/tx_source.js +13 -8
  222. package/dest/services/tx_file_store/tx_file_store.d.ts +1 -3
  223. package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -1
  224. package/dest/services/tx_file_store/tx_file_store.js +4 -14
  225. package/dest/services/tx_provider.d.ts +3 -1
  226. package/dest/services/tx_provider.d.ts.map +1 -1
  227. package/dest/services/tx_provider.js +3 -0
  228. package/dest/test-helpers/make-test-p2p-clients.d.ts +1 -1
  229. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  230. package/dest/test-helpers/make-test-p2p-clients.js +5 -2
  231. package/dest/test-helpers/mock-pubsub.d.ts +33 -9
  232. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  233. package/dest/test-helpers/mock-pubsub.js +57 -44
  234. package/dest/test-helpers/reqresp-nodes.d.ts +4 -5
  235. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  236. package/dest/test-helpers/reqresp-nodes.js +16 -18
  237. package/dest/test-helpers/test_tx_provider.d.ts +3 -1
  238. package/dest/test-helpers/test_tx_provider.d.ts.map +1 -1
  239. package/dest/test-helpers/test_tx_provider.js +3 -0
  240. package/dest/test-helpers/testbench-utils.d.ts +15 -14
  241. package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
  242. package/dest/test-helpers/testbench-utils.js +28 -13
  243. package/dest/testbench/p2p_client_testbench_worker.d.ts +3 -5
  244. package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -1
  245. package/dest/testbench/p2p_client_testbench_worker.js +101 -39
  246. package/dest/testbench/worker_client_manager.d.ts +10 -6
  247. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  248. package/dest/testbench/worker_client_manager.js +51 -9
  249. package/dest/util.d.ts +11 -6
  250. package/dest/util.d.ts.map +1 -1
  251. package/dest/util.js +35 -14
  252. package/dest/versioning.d.ts +3 -6
  253. package/dest/versioning.d.ts.map +1 -1
  254. package/dest/versioning.js +3 -24
  255. package/package.json +17 -17
  256. package/src/bootstrap/bootstrap.ts +17 -3
  257. package/src/client/factory.ts +53 -24
  258. package/src/client/interface.ts +48 -18
  259. package/src/client/p2p_client.ts +144 -86
  260. package/src/client/test/{tx_proposal_collector/README.md → p2p_client.batch_tx_requester.bench.README.md} +23 -53
  261. package/src/config.ts +159 -34
  262. package/src/errors/p2p-service.error.ts +11 -0
  263. package/src/errors/reqresp.error.ts +13 -19
  264. package/src/index.ts +0 -1
  265. package/src/mem_pools/attestation_pool/attestation_pool.ts +324 -239
  266. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +238 -63
  267. package/src/mem_pools/attestation_pool/mocks.ts +13 -8
  268. package/src/mem_pools/index.ts +0 -3
  269. package/src/mem_pools/instrumentation.ts +18 -12
  270. package/src/mem_pools/tx_pool_v2/archive/tx_archive.ts +30 -20
  271. package/src/mem_pools/tx_pool_v2/deleted_pool.ts +7 -7
  272. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +2 -4
  273. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +3 -2
  274. package/src/mem_pools/tx_pool_v2/eviction/index.ts +1 -0
  275. package/src/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.ts +67 -0
  276. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.ts +1 -3
  277. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +5 -7
  278. package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +5 -6
  279. package/src/mem_pools/tx_pool_v2/instrumentation.ts +27 -0
  280. package/src/mem_pools/tx_pool_v2/interfaces.ts +31 -4
  281. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +20 -8
  282. package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +35 -0
  283. package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +94 -31
  284. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +270 -133
  285. package/src/msg_validators/attestation_validator/attestation_validator.ts +46 -15
  286. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +15 -7
  287. package/src/msg_validators/proposal_validator/README.md +1 -1
  288. package/src/msg_validators/proposal_validator/block_proposal_validator.ts +15 -3
  289. package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +26 -3
  290. package/src/msg_validators/proposal_validator/proposal_validator.ts +94 -12
  291. package/src/msg_validators/tx_validator/README.md +13 -3
  292. package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +3 -12
  293. package/src/msg_validators/tx_validator/allowed_public_setup.ts +3 -3
  294. package/src/msg_validators/tx_validator/archive_cache.ts +1 -1
  295. package/src/msg_validators/tx_validator/cached_tx_validator.ts +31 -0
  296. package/src/msg_validators/tx_validator/data_validator.ts +2 -0
  297. package/src/msg_validators/tx_validator/factory.ts +37 -20
  298. package/src/msg_validators/tx_validator/gas_validator.ts +99 -54
  299. package/src/msg_validators/tx_validator/index.ts +2 -0
  300. package/src/msg_validators/tx_validator/phases_validator.ts +2 -2
  301. package/src/msg_validators/tx_validator/tx_proof_validator.ts +2 -0
  302. package/src/msg_validators/tx_validator/tx_validation_cache.ts +101 -0
  303. package/src/services/data_store.ts +5 -13
  304. package/src/services/discv5/discV5_service.ts +126 -14
  305. package/src/services/discv5/persisted_enr_store.ts +158 -0
  306. package/src/services/dummy_service.ts +26 -40
  307. package/src/services/encoding.ts +28 -20
  308. package/src/services/gossipsub/README.md +33 -11
  309. package/src/services/gossipsub/topic_score_params.ts +9 -18
  310. package/src/services/libp2p/instrumentation.ts +22 -0
  311. package/src/services/libp2p/libp2p_service.ts +456 -305
  312. package/src/services/peer-manager/peer_manager.ts +133 -22
  313. package/src/services/peer-manager/peer_scoring.ts +102 -8
  314. package/src/services/reqresp/README.md +2 -37
  315. package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +39 -52
  316. package/src/services/reqresp/batch-tx-requester/interface.ts +11 -4
  317. package/src/services/reqresp/batch-tx-requester/tx_validator.ts +12 -25
  318. package/src/services/reqresp/config.ts +19 -2
  319. package/src/services/reqresp/interface.ts +21 -47
  320. package/src/services/reqresp/metrics.ts +0 -1
  321. package/src/services/reqresp/protocols/block_txs/bitvector.ts +3 -2
  322. package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +34 -30
  323. package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +47 -20
  324. package/src/services/reqresp/protocols/index.ts +0 -1
  325. package/src/services/reqresp/protocols/tx.ts +1 -3
  326. package/src/services/reqresp/rate-limiter/rate_limiter.ts +13 -9
  327. package/src/services/reqresp/rate-limiter/rate_limits.ts +3 -13
  328. package/src/services/reqresp/reqresp.ts +69 -252
  329. package/src/services/service.ts +51 -35
  330. package/src/services/tx_collection/config.ts +3 -80
  331. package/src/services/tx_collection/file_store_tx_collection.ts +54 -103
  332. package/src/services/tx_collection/file_store_tx_source.ts +43 -31
  333. package/src/services/tx_collection/index.ts +1 -6
  334. package/src/services/tx_collection/instrumentation.ts +1 -7
  335. package/src/services/tx_collection/tx_collection.ts +330 -173
  336. package/src/services/tx_collection/tx_collection_sink.ts +2 -2
  337. package/src/services/tx_collection/tx_source.ts +10 -8
  338. package/src/services/tx_file_store/tx_file_store.ts +5 -17
  339. package/src/services/tx_provider.ts +5 -0
  340. package/src/test-helpers/make-test-p2p-clients.ts +3 -0
  341. package/src/test-helpers/mock-pubsub.ts +61 -60
  342. package/src/test-helpers/reqresp-nodes.ts +12 -22
  343. package/src/test-helpers/test_tx_provider.ts +5 -0
  344. package/src/test-helpers/testbench-utils.ts +35 -26
  345. package/src/testbench/p2p_client_testbench_worker.ts +108 -53
  346. package/src/testbench/worker_client_manager.ts +59 -20
  347. package/src/util.ts +32 -17
  348. package/src/versioning.ts +3 -33
  349. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts +0 -2
  350. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts.map +0 -1
  351. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +0 -304
  352. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts +0 -73
  353. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts.map +0 -1
  354. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.js +0 -8
  355. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +0 -125
  356. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +0 -1
  357. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +0 -596
  358. package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts +0 -32
  359. package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts.map +0 -1
  360. package/dest/mem_pools/tx_pool/eviction/eviction_manager.js +0 -112
  361. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts +0 -157
  362. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts.map +0 -1
  363. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.js +0 -52
  364. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts +0 -16
  365. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts.map +0 -1
  366. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.js +0 -123
  367. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts +0 -17
  368. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts.map +0 -1
  369. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.js +0 -84
  370. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts +0 -19
  371. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts.map +0 -1
  372. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.js +0 -78
  373. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts +0 -26
  374. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts.map +0 -1
  375. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.js +0 -84
  376. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts +0 -25
  377. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts.map +0 -1
  378. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.js +0 -57
  379. package/dest/mem_pools/tx_pool/index.d.ts +0 -3
  380. package/dest/mem_pools/tx_pool/index.d.ts.map +0 -1
  381. package/dest/mem_pools/tx_pool/index.js +0 -2
  382. package/dest/mem_pools/tx_pool/priority.d.ts +0 -12
  383. package/dest/mem_pools/tx_pool/priority.d.ts.map +0 -1
  384. package/dest/mem_pools/tx_pool/priority.js +0 -15
  385. package/dest/mem_pools/tx_pool/tx_pool.d.ts +0 -127
  386. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +0 -1
  387. package/dest/mem_pools/tx_pool/tx_pool.js +0 -3
  388. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +0 -7
  389. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +0 -1
  390. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +0 -402
  391. package/dest/msg_validators/clock_tolerance.d.ts +0 -21
  392. package/dest/msg_validators/clock_tolerance.d.ts.map +0 -1
  393. package/dest/msg_validators/clock_tolerance.js +0 -38
  394. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +0 -64
  395. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +0 -1
  396. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.js +0 -151
  397. package/dest/services/reqresp/protocols/block.d.ts +0 -9
  398. package/dest/services/reqresp/protocols/block.d.ts.map +0 -1
  399. package/dest/services/reqresp/protocols/block.js +0 -32
  400. package/dest/services/tx_collection/fast_tx_collection.d.ts +0 -51
  401. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +0 -1
  402. package/dest/services/tx_collection/fast_tx_collection.js +0 -311
  403. package/dest/services/tx_collection/proposal_tx_collector.d.ts +0 -48
  404. package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +0 -1
  405. package/dest/services/tx_collection/proposal_tx_collector.js +0 -50
  406. package/dest/services/tx_collection/slow_tx_collection.d.ts +0 -57
  407. package/dest/services/tx_collection/slow_tx_collection.d.ts.map +0 -1
  408. package/dest/services/tx_collection/slow_tx_collection.js +0 -211
  409. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +0 -343
  410. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.ts +0 -43
  411. package/src/mem_pools/tx_pool/README.md +0 -270
  412. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +0 -746
  413. package/src/mem_pools/tx_pool/eviction/eviction_manager.ts +0 -132
  414. package/src/mem_pools/tx_pool/eviction/eviction_strategy.ts +0 -208
  415. package/src/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.ts +0 -163
  416. package/src/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.ts +0 -104
  417. package/src/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.ts +0 -93
  418. package/src/mem_pools/tx_pool/eviction/low_priority_eviction_rule.ts +0 -106
  419. package/src/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.ts +0 -75
  420. package/src/mem_pools/tx_pool/index.ts +0 -2
  421. package/src/mem_pools/tx_pool/priority.ts +0 -20
  422. package/src/mem_pools/tx_pool/tx_pool.ts +0 -141
  423. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +0 -321
  424. package/src/msg_validators/clock_tolerance.ts +0 -52
  425. package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +0 -161
  426. package/src/services/reqresp/protocols/block.ts +0 -37
  427. package/src/services/tx_collection/fast_tx_collection.ts +0 -361
  428. package/src/services/tx_collection/proposal_tx_collector.ts +0 -108
  429. package/src/services/tx_collection/slow_tx_collection.ts +0 -266
@@ -1,33 +1,41 @@
1
1
  import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
2
  import { BlockNumber, type SlotNumber } from '@aztec/foundation/branded-types';
3
- import { maxBy } from '@aztec/foundation/collection';
4
- import { Fr } from '@aztec/foundation/curves/bn254';
3
+ import { compactArray, maxBy, merge } from '@aztec/foundation/collection';
5
4
  import { type Logger, createLibp2pComponentLogger, createLogger } from '@aztec/foundation/log';
6
5
  import { RunningPromise } from '@aztec/foundation/running-promise';
7
6
  import { Timer } from '@aztec/foundation/timer';
8
7
  import type { AztecAsyncKVStore } from '@aztec/kv-store';
9
8
  import { protocolContractsHash } from '@aztec/protocol-contracts';
10
- import type { EthAddress, L2Block, L2BlockSource } from '@aztec/stdlib/block';
9
+ import type { EthAddress, L2BlockSource } from '@aztec/stdlib/block';
10
+ import { DEFAULT_MAX_BLOCKS_PER_CHECKPOINT } from '@aztec/stdlib/config';
11
11
  import type { ContractDataSource } from '@aztec/stdlib/contract';
12
- import { GasFees } from '@aztec/stdlib/gas';
13
- import type { ClientProtocolCircuitVerifier, PeerInfo, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
12
+ import { type BlockMinFeesProvider, GasFees, getNetworkTxGasLimits } from '@aztec/stdlib/gas';
13
+ import type {
14
+ ClientProtocolCircuitVerifier,
15
+ P2PConnectivity,
16
+ PeerInfo,
17
+ WorldStateSynchronizer,
18
+ } from '@aztec/stdlib/interfaces/server';
14
19
  import {
15
20
  BlockProposal,
16
21
  CheckpointAttestation,
17
22
  CheckpointProposal,
18
- type CheckpointProposalCore,
19
23
  type Gossipable,
20
24
  P2PMessage,
21
25
  PeerErrorSeverity,
22
26
  PeerErrorSeverityByHarshness,
23
27
  TopicType,
28
+ ValidatedBlockProposal,
29
+ ValidatedCheckpointProposalCore,
24
30
  createTopicString,
25
31
  getTopicsForConfig,
26
32
  metricsTopicStrToLabels,
27
33
  } from '@aztec/stdlib/p2p';
34
+ import { ConsensusTimetable, getDefaultCheckpointProposalSyncGrace } from '@aztec/stdlib/timetable';
28
35
  import { MerkleTreeId } from '@aztec/stdlib/trees';
29
- import { Tx, type TxHash, type TxValidationResult, type TxValidator } from '@aztec/stdlib/tx';
36
+ import { Tx, type TxValidationResult } from '@aztec/stdlib/tx';
30
37
  import type { UInt64 } from '@aztec/stdlib/types';
38
+ import { InvalidBlockProposalTxsError } from '@aztec/stdlib/validators';
31
39
  import { compressComponentVersions } from '@aztec/stdlib/versioning';
32
40
  import {
33
41
  Attributes,
@@ -51,13 +59,15 @@ import { yamux } from '@chainsafe/libp2p-yamux';
51
59
  import { bootstrap } from '@libp2p/bootstrap';
52
60
  import { identify } from '@libp2p/identify';
53
61
  import { type Message, type MultiaddrConnection, type PeerId, TopicValidatorResult } from '@libp2p/interface';
54
- import type { ConnectionManager } from '@libp2p/interface-internal';
62
+ import type { AddressManager, ConnectionManager } from '@libp2p/interface-internal';
55
63
  import { mplex } from '@libp2p/mplex';
56
64
  import { tcp } from '@libp2p/tcp';
65
+ import { multiaddr } from '@multiformats/multiaddr';
57
66
  import { ENR } from '@nethermindeth/enr';
58
67
  import { createLibp2p } from 'libp2p';
59
68
 
60
69
  import type { P2PConfig } from '../../config.js';
70
+ import { CheckpointProposalReceivedCallbackNotRegisteredError } from '../../errors/p2p-service.error.js';
61
71
  import type { MemPools } from '../../mem_pools/interface.js';
62
72
  import {
63
73
  BlockProposalValidator,
@@ -73,38 +83,34 @@ import {
73
83
  createFirstStageTxValidationsForGossipedTransactions,
74
84
  createSecondStageTxValidationsForGossipedTransactions,
75
85
  createTxValidatorForBlockProposalReceivedTxs,
76
- createTxValidatorForReqResponseReceivedTxs,
77
86
  } from '../../msg_validators/tx_validator/factory.js';
87
+ import { TxValidationCache } from '../../msg_validators/tx_validator/tx_validation_cache.js';
78
88
  import { GossipSubEvent } from '../../types/index.js';
79
89
  import { type PubSubLibp2p, convertToMultiaddr } from '../../util.js';
80
90
  import { getVersions } from '../../versioning.js';
81
91
  import { AztecDatastore } from '../data_store.js';
82
92
  import { DiscV5Service } from '../discv5/discV5_service.js';
83
- import { SnappyTransform, fastMsgIdFn, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
93
+ import { SnappyTransform, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
84
94
  import { APP_SPECIFIC_WEIGHT, gossipScoreThresholds } from '../gossipsub/scoring.js';
85
95
  import { createAllTopicScoreParams } from '../gossipsub/topic_score_params.js';
86
96
  import type { PeerManagerInterface } from '../peer-manager/interface.js';
87
97
  import { PeerManager } from '../peer-manager/peer_manager.js';
88
98
  import { PeerScoring } from '../peer-manager/peer_scoring.js';
89
99
  import type { BatchTxRequesterLibP2PService } from '../reqresp/batch-tx-requester/interface.js';
90
- import type { P2PReqRespConfig } from '../reqresp/config.js';
100
+ import { type P2PReqRespConfig, YAMUX_MAX_MESSAGE_SIZE_BYTES } from '../reqresp/config.js';
91
101
  import {
92
102
  AuthRequest,
93
103
  BlockTxsRequest,
94
104
  BlockTxsResponse,
95
- DEFAULT_SUB_PROTOCOL_VALIDATORS,
96
105
  type ReqRespInterface,
97
106
  type ReqRespResponse,
98
107
  ReqRespSubProtocol,
99
108
  type ReqRespSubProtocolHandler,
100
109
  type ReqRespSubProtocolHandlers,
101
- type ReqRespSubProtocolValidators,
102
110
  StatusMessage,
103
- type SubProtocolMap,
104
111
  ValidationError,
105
112
  pingHandler,
106
113
  reqGoodbyeHandler,
107
- reqRespBlockHandler,
108
114
  reqRespBlockTxsHandler,
109
115
  reqRespStatusHandler,
110
116
  reqRespTxHandler,
@@ -112,13 +118,33 @@ import {
112
118
  import { ReqResp } from '../reqresp/reqresp.js';
113
119
  import type {
114
120
  P2PBlockReceivedCallback,
121
+ P2PCheckpointAttestationCallback,
115
122
  P2PCheckpointReceivedCallback,
116
123
  P2PDuplicateAttestationCallback,
124
+ P2POversizedProposalCallback,
117
125
  P2PService,
118
126
  PeerDiscoveryService,
119
127
  } from '../service.js';
120
128
  import { P2PInstrumentation } from './instrumentation.js';
121
129
 
130
+ /**
131
+ * Builds the {@link ConsensusTimetable} shared by the gossip validators for proposal/attestation receive-window
132
+ * bounds. Derived purely from protocol slot-timing constants plus the block sub-slot duration and the consensus
133
+ * materialization grace, so every node agrees on these bounds without depending on proposer operational budgets.
134
+ */
135
+ function buildConsensusTimetable(
136
+ config: P2PConfig,
137
+ l1Constants: ReturnType<EpochCacheInterface['getL1Constants']>,
138
+ ): ConsensusTimetable {
139
+ const blockDuration = config.blockDurationMs / 1000;
140
+ return new ConsensusTimetable({
141
+ l1Constants,
142
+ blockDuration,
143
+ checkpointProposalSyncGrace:
144
+ config.checkpointProposalSyncGraceSeconds ?? getDefaultCheckpointProposalSyncGrace(blockDuration),
145
+ });
146
+ }
147
+
122
148
  interface ValidationResult {
123
149
  name: string;
124
150
  isValid: TxValidationResult;
@@ -147,8 +173,6 @@ export class LibP2PService extends WithTracer implements P2PService {
147
173
  private protocolVersion = '';
148
174
  private topicStrings: Record<TopicType, string> = {} as Record<TopicType, string>;
149
175
 
150
- private feesCache: { blockNumber: BlockNumber; gasFees: GasFees } | undefined;
151
-
152
176
  /** Callback invoked when a duplicate proposal is detected (triggers slashing). */
153
177
  private duplicateProposalCallback?: (info: {
154
178
  slot: SlotNumber;
@@ -156,9 +180,15 @@ export class LibP2PService extends WithTracer implements P2PService {
156
180
  type: 'checkpoint' | 'block';
157
181
  }) => void;
158
182
 
183
+ /** Callback invoked when an oversized block proposal is stored as slashing evidence (triggers slashing). */
184
+ private oversizedProposalCallback?: P2POversizedProposalCallback;
185
+
159
186
  /** Callback invoked when a duplicate attestation is detected (triggers slashing). */
160
187
  private duplicateAttestationCallback?: P2PDuplicateAttestationCallback;
161
188
 
189
+ /** Callback invoked when a valid checkpoint attestation is accepted into the pool. */
190
+ private checkpointAttestationCallback?: P2PCheckpointAttestationCallback;
191
+
162
192
  /**
163
193
  * Callback for when a block is received from a peer.
164
194
  * @param block - The block received from the peer.
@@ -171,10 +201,19 @@ export class LibP2PService extends WithTracer implements P2PService {
171
201
  * @param checkpoint - The checkpoint proposal received from the peer.
172
202
  * @returns The attestations for the checkpoint, if any.
173
203
  */
174
- private checkpointReceivedCallback: P2PCheckpointReceivedCallback;
204
+ private allNodesCheckpointReceivedCallback: P2PCheckpointReceivedCallback;
205
+ /**
206
+ * Callback for when a checkpoint proposal is received - specifically for validators - from a peer.
207
+ * @param checkpoint - The checkpoint proposal received from the peer.
208
+ * @returns The attestations for the checkpoint, if any.
209
+ */
210
+ private validatorCheckpointReceivedCallback: P2PCheckpointReceivedCallback;
175
211
 
176
212
  private gossipSubEventHandler: (e: CustomEvent<GossipsubMessage>) => void;
177
213
 
214
+ private ipChangedHandler?: (ip: string) => void;
215
+ private discoveredP2pIp?: string;
216
+
178
217
  private instrumentation: P2PInstrumentation;
179
218
 
180
219
  private telemetry: TelemetryClient;
@@ -192,8 +231,10 @@ export class LibP2PService extends WithTracer implements P2PService {
192
231
  private epochCache: EpochCacheInterface,
193
232
  private proofVerifier: ClientProtocolCircuitVerifier,
194
233
  private worldStateSynchronizer: WorldStateSynchronizer,
234
+ private blockMinFeesProvider: BlockMinFeesProvider,
195
235
  telemetry: TelemetryClient,
196
236
  logger: Logger = createLogger('p2p:libp2p_service'),
237
+ private txValidationCache?: TxValidationCache,
197
238
  ) {
198
239
  super(telemetry, 'LibP2PService');
199
240
  this.telemetry = telemetry;
@@ -223,15 +264,39 @@ export class LibP2PService extends WithTracer implements P2PService {
223
264
  this.protocolVersion,
224
265
  );
225
266
 
267
+ // Build the consensus timetable once from protocol slot-timing constants and inject it into every
268
+ // validator so they share one set of receive-window bounds, independent of proposer operational budgets.
269
+ const consensusTimetable = buildConsensusTimetable(config, epochCache.getL1Constants());
226
270
  const proposalValidatorOpts = {
227
271
  txsPermitted: !config.disableTransactions,
228
272
  maxTxsPerBlock: config.validateMaxTxsPerBlock ?? config.validateMaxTxsPerCheckpoint,
273
+ maxBlocksPerCheckpoint: config.maxBlocksPerCheckpoint,
274
+ skipSlotValidation: config.skipProposalSlotValidation,
275
+ signatureContext: {
276
+ chainId: config.l1ChainId,
277
+ rollupAddress: config.rollupAddress,
278
+ },
279
+ clockDisparityMs: config.maxGossipClockDisparityMs,
280
+ };
281
+ this.blockProposalValidator = new BlockProposalValidator(epochCache, consensusTimetable, proposalValidatorOpts);
282
+ this.checkpointProposalValidator = new CheckpointProposalValidator(
283
+ epochCache,
284
+ consensusTimetable,
285
+ proposalValidatorOpts,
286
+ );
287
+ const attestationValidatorOpts = {
288
+ signatureContext: proposalValidatorOpts.signatureContext,
289
+ clockDisparityMs: config.maxGossipClockDisparityMs,
229
290
  };
230
- this.blockProposalValidator = new BlockProposalValidator(epochCache, proposalValidatorOpts);
231
- this.checkpointProposalValidator = new CheckpointProposalValidator(epochCache, proposalValidatorOpts);
232
291
  this.checkpointAttestationValidator = config.fishermanMode
233
- ? new FishermanAttestationValidator(epochCache, mempools.attestationPool, telemetry)
234
- : new CheckpointAttestationValidator(epochCache);
292
+ ? new FishermanAttestationValidator(
293
+ epochCache,
294
+ consensusTimetable,
295
+ mempools.attestationPool,
296
+ telemetry,
297
+ attestationValidatorOpts,
298
+ )
299
+ : new CheckpointAttestationValidator(epochCache, consensusTimetable, attestationValidatorOpts);
235
300
 
236
301
  this.gossipSubEventHandler = this.handleGossipSubEvent.bind(this);
237
302
 
@@ -243,18 +308,22 @@ export class LibP2PService extends WithTracer implements P2PService {
243
308
  return true;
244
309
  };
245
310
 
246
- this.checkpointReceivedCallback = (
247
- checkpoint: CheckpointProposalCore,
311
+ this.allNodesCheckpointReceivedCallback = (
312
+ _checkpoint: ValidatedCheckpointProposalCore,
313
+ ): Promise<CheckpointAttestation[] | undefined> => {
314
+ throw new CheckpointProposalReceivedCallbackNotRegisteredError();
315
+ };
316
+
317
+ this.validatorCheckpointReceivedCallback = (
318
+ _checkpoint: ValidatedCheckpointProposalCore,
248
319
  ): Promise<CheckpointAttestation[] | undefined> => {
249
- this.logger.debug(
250
- `Handler not yet registered: Checkpoint received callback not set. Received checkpoint for slot ${checkpoint.slotNumber} from peer.`,
251
- );
252
320
  return Promise.resolve(undefined);
253
321
  };
254
322
  }
255
323
 
256
- public updateConfig(config: Partial<P2PReqRespConfig>) {
324
+ public updateConfig(config: Partial<P2PReqRespConfig & Pick<P2PConfig, 'skipIncomingProposals'>>) {
257
325
  this.reqresp.updateConfig(config);
326
+ this.config = merge(this.config, config);
258
327
  }
259
328
 
260
329
  /**
@@ -273,9 +342,11 @@ export class LibP2PService extends WithTracer implements P2PService {
273
342
  proofVerifier: ClientProtocolCircuitVerifier;
274
343
  worldStateSynchronizer: WorldStateSynchronizer;
275
344
  peerStore: AztecAsyncKVStore;
345
+ blockMinFeesProvider: BlockMinFeesProvider;
276
346
  telemetry: TelemetryClient;
277
347
  logger: Logger;
278
348
  packageVersion: string;
349
+ txValidationCache?: TxValidationCache;
279
350
  },
280
351
  ) {
281
352
  const {
@@ -285,9 +356,11 @@ export class LibP2PService extends WithTracer implements P2PService {
285
356
  mempools,
286
357
  proofVerifier,
287
358
  peerStore,
359
+ blockMinFeesProvider,
288
360
  telemetry,
289
361
  logger,
290
362
  packageVersion,
363
+ txValidationCache,
291
364
  } = deps;
292
365
  const { p2pPort, maxPeerCount, listenAddress } = config;
293
366
  const bindAddrTcp = convertToMultiaddr(listenAddress, p2pPort, 'tcp');
@@ -302,6 +375,7 @@ export class LibP2PService extends WithTracer implements P2PService {
302
375
  packageVersion,
303
376
  telemetry,
304
377
  createLogger(`${logger.module}:discv5_service`, logger.getBindings()),
378
+ peerStore,
305
379
  );
306
380
 
307
381
  // Seed libp2p's bootstrap discovery with private and trusted peers
@@ -316,34 +390,49 @@ export class LibP2PService extends WithTracer implements P2PService {
316
390
  const protocolVersion = compressComponentVersions(versions);
317
391
 
318
392
  const preferredPeersEnrs: ENR[] = config.preferredPeers.map(enr => ENR.decodeTxt(enr));
319
- const directPeers = (
393
+ const directPeers = compactArray(
320
394
  await Promise.all(
321
395
  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()}`);
396
+ try {
397
+ const peerId = await enr.peerId();
398
+ const address = enr.getLocationMultiaddr('tcp');
399
+ if (address === undefined) {
400
+ throw new Error(`Direct peer ${peerId.toString()} has no TCP address, ENR: ${enr.encodeTxt()}`);
401
+ }
402
+ return {
403
+ id: peerId,
404
+ addrs: [address],
405
+ };
406
+ } catch (err) {
407
+ // A malformed configured ENR shouldn't abort node setup — skip it and log.
408
+ logger.warn(`Skipping preferred peer with invalid ENR`, { err });
409
+ return undefined;
326
410
  }
327
- return {
328
- id: peerId,
329
- addrs: [address],
330
- };
331
411
  }),
332
- )
333
- ).filter(peer => peer !== undefined);
412
+ ),
413
+ );
334
414
 
335
415
  const announceTcpMultiaddr = config.p2pIp ? [convertToMultiaddr(config.p2pIp, p2pPort, 'tcp')] : [];
336
416
 
337
- // Create dynamic topic score params based on network configuration
417
+ // Create dynamic topic score params based on network configuration. Scoring uses the network-wide
418
+ // max-blocks-per-checkpoint config value directly to size expected per-slot message rates; these are
419
+ // peer-rate thresholds, not consensus deadlines, so they need no proposer operational budgets.
338
420
  const l1Constants = epochCache.getL1Constants();
339
421
  const topicScoreParams = createAllTopicScoreParams(protocolVersion, {
340
422
  slotDurationMs: l1Constants.slotDuration * 1000,
341
423
  heartbeatIntervalMs: config.gossipsubInterval,
342
424
  targetCommitteeSize: l1Constants.targetCommitteeSize,
343
- blockDurationMs: config.blockDurationMs,
425
+ maxBlocksPerCheckpoint: config.maxBlocksPerCheckpoint ?? DEFAULT_MAX_BLOCKS_PER_CHECKPOINT,
344
426
  expectedBlockProposalsPerSlot: config.expectedBlockProposalsPerSlot,
345
427
  });
346
428
 
429
+ // Restrict gossipsub to exactly the topics we subscribe to. Without this, an arbitrary-topic
430
+ // message is transformed, msg-id'd and inserted into the seenCache before the subscription check,
431
+ // so a crafted topic colliding on msg id can suppress a real message as a duplicate.
432
+ const allowedTopics = getTopicsForConfig(config.disableTransactions).map(topic =>
433
+ createTopicString(topic, protocolVersion),
434
+ );
435
+
347
436
  const node = await createLibp2p({
348
437
  start: false,
349
438
  peerId,
@@ -375,7 +464,8 @@ export class LibP2PService extends WithTracer implements P2PService {
375
464
  ],
376
465
  datastore,
377
466
  peerDiscovery,
378
- streamMuxers: [yamux(), mplex()],
467
+ // Pin the yamux frame size: MAX_REQRESP_REQUEST_SIZE_BYTES relies on a reqresp request fitting in one frame.
468
+ streamMuxers: [yamux({ maxMessageSize: YAMUX_MAX_MESSAGE_SIZE_BYTES }), mplex()],
379
469
  connectionEncryption: [noise()],
380
470
  connectionManager: {
381
471
  minConnections: 0, // Disable libp2p peer dialing, we do it manually
@@ -389,7 +479,7 @@ export class LibP2PService extends WithTracer implements P2PService {
389
479
  },
390
480
  connectionGater: {
391
481
  denyInboundConnection: (maConn: MultiaddrConnection) => {
392
- const allowed = peerManager.isNodeAllowedToConnect(maConn.remoteAddr.nodeAddress().address);
482
+ const allowed = peerManager.isAddressAllowedToConnect(maConn.remoteAddr.nodeAddress().address);
393
483
  if (allowed) {
394
484
  return false;
395
485
  }
@@ -400,7 +490,7 @@ export class LibP2PService extends WithTracer implements P2PService {
400
490
  denyInboundEncryptedConnection: (peerId: PeerId, _maConn: MultiaddrConnection) => {
401
491
  //NOTE: it is not necessary to check address here because this was already done by
402
492
  // denyInboundConnection
403
- const allowed = peerManager.isNodeAllowedToConnect(peerId);
493
+ const allowed = peerManager.isPeerAllowedToConnect(peerId);
404
494
  if (allowed) {
405
495
  return false;
406
496
  }
@@ -428,9 +518,13 @@ export class LibP2PService extends WithTracer implements P2PService {
428
518
  mcacheLength: config.gossipsubMcacheLength,
429
519
  mcacheGossip: config.gossipsubMcacheGossip,
430
520
  seenTTL: config.gossipsubSeenTTL,
521
+ allowedTopics,
522
+ // No fastMsgIdFn: the fast-path dedup cache keys on a non-cryptographic 64-bit hash of the
523
+ // raw data only (no topic), so a collision — accidental or engineered via a weak seed — drops
524
+ // a different message with no fallback to the full id. Dedup instead rests solely on the
525
+ // cryptographic, topic-framed msgIdFn below.
431
526
  msgIdFn: getMsgIdFn,
432
527
  msgIdToStrFn: msgIdToStrFn,
433
- fastMsgIdFn: fastMsgIdFn,
434
528
  dataTransform: new SnappyTransform(),
435
529
  metricsRegister: otelMetricsAdapter,
436
530
  metricsTopicStrToLabel: metricsTopicStrToLabels(protocolVersion),
@@ -442,8 +536,9 @@ export class LibP2PService extends WithTracer implements P2PService {
442
536
  topics: topicScoreParams,
443
537
  }),
444
538
  }) as (components: GossipSubComponents) => GossipSub,
445
- components: (components: { connectionManager: ConnectionManager }) => ({
539
+ components: (components: { connectionManager: ConnectionManager; addressManager: AddressManager }) => ({
446
540
  connectionManager: components.connectionManager,
541
+ addressManager: components.addressManager,
447
542
  }),
448
543
  },
449
544
  logger: createLibp2pComponentLogger(logger.module, logger.getBindings()),
@@ -465,6 +560,9 @@ export class LibP2PService extends WithTracer implements P2PService {
465
560
  epochCache,
466
561
  );
467
562
 
563
+ // Gate req/resp data protocols for unauthenticated peers when p2pAllowOnlyValidators is enabled
564
+ reqresp.setShouldRejectPeer(peerId => peerManager.shouldDisableP2PGossip(peerId));
565
+
468
566
  // Configure application-specific scoring for gossipsub.
469
567
  // The weight scales app score to align with gossipsub thresholds:
470
568
  // - Disconnect (-50) × 10 = -500 = gossipThreshold (stops receiving gossip)
@@ -485,8 +583,10 @@ export class LibP2PService extends WithTracer implements P2PService {
485
583
  epochCache,
486
584
  proofVerifier,
487
585
  worldStateSynchronizer,
586
+ blockMinFeesProvider,
488
587
  telemetry,
489
588
  logger,
589
+ txValidationCache,
490
590
  );
491
591
  }
492
592
 
@@ -500,24 +600,21 @@ export class LibP2PService extends WithTracer implements P2PService {
500
600
  throw new Error('P2P service already started');
501
601
  }
502
602
 
503
- // Get listen & announce addresses for logging
504
603
  const { p2pIp, p2pPort } = this.config;
505
- if (!p2pIp) {
506
- throw new Error('Announce address not provided.');
604
+ if (!p2pIp && !this.config.queryForIp) {
605
+ throw new Error('Announce address not provided and queryForIp is not enabled.');
507
606
  }
508
- const announceTcpMultiaddr = convertToMultiaddr(p2pIp, p2pPort, 'tcp');
607
+ const announceTcpMultiaddr = p2pIp ? convertToMultiaddr(p2pIp, p2pPort, 'tcp') : undefined;
509
608
 
510
609
  // Create request response protocol handlers
511
610
  const txHandler = reqRespTxHandler(this.mempools);
512
611
  const goodbyeHandler = reqGoodbyeHandler(this.peerManager);
513
- const blockHandler = reqRespBlockHandler(this.archiver);
514
612
  const statusHandler = reqRespStatusHandler(this.protocolVersion, this.worldStateSynchronizer, this.logger);
515
613
 
516
614
  const requestResponseHandlers: Partial<ReqRespSubProtocolHandlers> = {
517
615
  [ReqRespSubProtocol.PING]: pingHandler,
518
616
  [ReqRespSubProtocol.STATUS]: statusHandler.bind(this),
519
617
  [ReqRespSubProtocol.GOODBYE]: goodbyeHandler.bind(this),
520
- [ReqRespSubProtocol.BLOCK]: blockHandler.bind(this),
521
618
  };
522
619
 
523
620
  if (!this.config.disableTransactions) {
@@ -533,17 +630,9 @@ export class LibP2PService extends WithTracer implements P2PService {
533
630
  requestResponseHandlers[ReqRespSubProtocol.TX] = txHandler.bind(this);
534
631
  }
535
632
 
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
633
  await this.peerManager.initializePeers();
545
634
 
546
- await this.reqresp.start(requestResponseHandlers, reqrespSubProtocolValidators);
635
+ await this.reqresp.start(requestResponseHandlers);
547
636
 
548
637
  await this.node.start();
549
638
 
@@ -559,6 +648,38 @@ export class LibP2PService extends WithTracer implements P2PService {
559
648
  if (!this.config.p2pDiscoveryDisabled) {
560
649
  await this.peerDiscoveryService.start();
561
650
  }
651
+
652
+ // Bridge discv5 IP changes to libp2p's AddressManager so peers see the updated address
653
+ if (this.config.queryForIp) {
654
+ this.discoveredP2pIp = this.config.p2pIp;
655
+ this.logger.info('IP change tracking enabled, bridging discv5 IP updates to libp2p AddressManager');
656
+ this.ipChangedHandler = (ip: string) => {
657
+ const addressManager = this.node.services.components.addressManager;
658
+ const newAddr = multiaddr(convertToMultiaddr(ip, this.config.p2pPort, 'tcp'));
659
+ const previousIp = this.discoveredP2pIp;
660
+
661
+ if (previousIp) {
662
+ const oldAddr = multiaddr(convertToMultiaddr(previousIp, this.config.p2pPort, 'tcp'));
663
+ addressManager.removeObservedAddr(oldAddr);
664
+ this.logger.info('Libp2p announce address updated due to IP change', {
665
+ previousIp,
666
+ newIp: ip,
667
+ newMultiaddr: newAddr.toString(),
668
+ });
669
+ } else {
670
+ this.logger.info('Libp2p announce address set from initial discv5 IP discovery', {
671
+ ip,
672
+ multiaddr: newAddr.toString(),
673
+ });
674
+ }
675
+
676
+ addressManager.addObservedAddr(newAddr);
677
+ addressManager.confirmObservedAddr(newAddr);
678
+ this.discoveredP2pIp = ip;
679
+ };
680
+ this.peerDiscoveryService.on('ip:changed', this.ipChangedHandler);
681
+ }
682
+
562
683
  this.discoveryRunningPromise = new RunningPromise(
563
684
  async () => {
564
685
  await this.peerManager.heartbeat();
@@ -584,6 +705,11 @@ export class LibP2PService extends WithTracer implements P2PService {
584
705
  // Remove gossip sub listener
585
706
  this.node.services.pubsub.removeEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
586
707
 
708
+ if (this.ipChangedHandler) {
709
+ this.peerDiscoveryService.removeListener('ip:changed', this.ipChangedHandler);
710
+ this.ipChangedHandler = undefined;
711
+ }
712
+
587
713
  // Stop peer manager
588
714
  this.logger.debug('Stopping peer manager...');
589
715
  await this.peerManager.stop();
@@ -598,12 +724,8 @@ export class LibP2PService extends WithTracer implements P2PService {
598
724
  this.logger.info('LibP2P service stopped');
599
725
  }
600
726
 
601
- addReqRespSubProtocol(
602
- subProtocol: ReqRespSubProtocol,
603
- handler: ReqRespSubProtocolHandler,
604
- validator?: ReqRespSubProtocolValidators[ReqRespSubProtocol],
605
- ): Promise<void> {
606
- return this.reqresp.addSubProtocol(subProtocol, handler, validator);
727
+ addReqRespSubProtocol(subProtocol: ReqRespSubProtocol, handler: ReqRespSubProtocolHandler): Promise<void> {
728
+ return this.reqresp.addSubProtocol(subProtocol, handler);
607
729
  }
608
730
 
609
731
  public registerThisValidatorAddresses(address: EthAddress[]): void {
@@ -614,6 +736,11 @@ export class LibP2PService extends WithTracer implements P2PService {
614
736
  return this.peerManager.getPeers(includePending);
615
737
  }
616
738
 
739
+ public getP2PConnectivity(): P2PConnectivity {
740
+ const connectedPeers = this.peerManager.getPeers().filter(peer => peer.status === 'connected').length;
741
+ return { enabled: true, connectedPeers };
742
+ }
743
+
617
744
  public getGossipMeshPeerCount(topicType: TopicType): number {
618
745
  return this.node.services.pubsub.getMeshPeers(this.topicStrings[topicType]).length;
619
746
  }
@@ -631,20 +758,6 @@ export class LibP2PService extends WithTracer implements P2PService {
631
758
  setImmediate(() => void safeJob());
632
759
  }
633
760
 
634
- /**
635
- * Send a batch of requests to peers, and return the responses
636
- * @param protocol - The request response protocol to use
637
- * @param requests - The requests to send to the peers
638
- * @returns The responses to the requests
639
- */
640
- sendBatchRequest<SubProtocol extends ReqRespSubProtocol>(
641
- protocol: SubProtocol,
642
- requests: InstanceType<SubProtocolMap[SubProtocol]['request']>[],
643
- pinnedPeerId: PeerId | undefined,
644
- ): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']>[]> {
645
- return this.reqresp.sendBatchRequest(protocol, requests, pinnedPeerId);
646
- }
647
-
648
761
  public sendRequestToPeer(
649
762
  peerId: PeerId,
650
763
  subProtocol: ReqRespSubProtocol,
@@ -666,8 +779,12 @@ export class LibP2PService extends WithTracer implements P2PService {
666
779
  this.blockReceivedCallback = callback;
667
780
  }
668
781
 
669
- public registerCheckpointReceivedCallback(callback: P2PCheckpointReceivedCallback) {
670
- this.checkpointReceivedCallback = callback;
782
+ public registerValidatorCheckpointReceivedCallback(callback: P2PCheckpointReceivedCallback) {
783
+ this.validatorCheckpointReceivedCallback = callback;
784
+ }
785
+
786
+ public registerAllNodesCheckpointReceivedCallback(callback: P2PCheckpointReceivedCallback) {
787
+ this.allNodesCheckpointReceivedCallback = callback;
671
788
  }
672
789
 
673
790
  /**
@@ -680,6 +797,13 @@ export class LibP2PService extends WithTracer implements P2PService {
680
797
  this.duplicateProposalCallback = callback;
681
798
  }
682
799
 
800
+ /**
801
+ * Registers a callback to be invoked when an oversized block proposal is stored as slashing evidence.
802
+ */
803
+ public registerOversizedProposalCallback(callback: P2POversizedProposalCallback): void {
804
+ this.oversizedProposalCallback = callback;
805
+ }
806
+
683
807
  /**
684
808
  * Registers a callback to be invoked when a duplicate attestation is detected.
685
809
  * A validator signing attestations for different proposals at the same slot.
@@ -689,6 +813,10 @@ export class LibP2PService extends WithTracer implements P2PService {
689
813
  this.duplicateAttestationCallback = callback;
690
814
  }
691
815
 
816
+ public registerCheckpointAttestationCallback(callback: P2PCheckpointAttestationCallback): void {
817
+ this.checkpointAttestationCallback = callback;
818
+ }
819
+
692
820
  /**
693
821
  * Subscribes to a topic.
694
822
  * @param topic - The topic to subscribe to.
@@ -817,6 +945,15 @@ export class LibP2PService extends WithTracer implements P2PService {
817
945
 
818
946
  // Process the message, optionally within a linked span for trace propagation
819
947
  const processMessage = async () => {
948
+ if (
949
+ this.config.skipIncomingProposals &&
950
+ (msg.topic === this.topicStrings[TopicType.block_proposal] ||
951
+ msg.topic === this.topicStrings[TopicType.checkpoint_proposal])
952
+ ) {
953
+ this.logger.warn(`Ignoring incoming proposal (skipIncomingProposals is set)`, { topic: msg.topic });
954
+ this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Ignore);
955
+ return;
956
+ }
820
957
  if (msg.topic === this.topicStrings[TopicType.tx]) {
821
958
  await this.handleGossipedTx(p2pMessage.payload, msgId, source);
822
959
  } else if (msg.topic === this.topicStrings[TopicType.checkpoint_attestation]) {
@@ -881,6 +1018,7 @@ export class LibP2PService extends WithTracer implements P2PService {
881
1018
  msgId: string,
882
1019
  source: PeerId,
883
1020
  topicType: TopicType,
1021
+ stageTimings?: Record<string, number>,
884
1022
  ): Promise<ReceivedMessageValidationResult<T, M>> {
885
1023
  // Default to reject result with a penalty if validation function throws an error
886
1024
  let resultAndObj: ReceivedMessageValidationResult<T, M> = {
@@ -894,8 +1032,25 @@ export class LibP2PService extends WithTracer implements P2PService {
894
1032
  this.logger.error(`Error validating gossipsub message`, err, { msgId, source: source.toString(), topicType });
895
1033
  }
896
1034
 
1035
+ const validationTimeMs = timer.ms();
1036
+ const mcacheWindowMs = this.config.gossipsubMcacheLength * this.config.gossipsubInterval;
1037
+ if (validationTimeMs > mcacheWindowMs * 0.75) {
1038
+ this.instrumentation.incSlowValidation(topicType);
1039
+ this.logger.warn(
1040
+ `Gossip validation for ${topicType} took ${validationTimeMs}ms, approaching mcache eviction window of ${mcacheWindowMs}ms. ` +
1041
+ `Message forwarding may be skipped if validation exceeds the window.`,
1042
+ { msgId, source: source.toString(), topicType, validationTimeMs, mcacheWindowMs, stageTimings },
1043
+ );
1044
+ }
1045
+
897
1046
  if (resultAndObj.result === TopicValidatorResult.Accept) {
898
- this.logger.debug(`Message ${topicType} accepted by validator`, { msgId, source: source.toString(), topicType });
1047
+ this.logger.debug(`Message ${topicType} accepted by validator`, {
1048
+ msgId,
1049
+ source: source.toString(),
1050
+ topicType,
1051
+ validationTimeMs,
1052
+ stageTimings,
1053
+ });
899
1054
  this.instrumentation.recordMessageValidation(topicType, timer);
900
1055
  } else if (resultAndObj.result === TopicValidatorResult.Reject) {
901
1056
  this.logger.warn(`Message ${topicType} rejected by validator with severity ${resultAndObj.severity}`, {
@@ -927,18 +1082,38 @@ export class LibP2PService extends WithTracer implements P2PService {
927
1082
  }
928
1083
 
929
1084
  protected async handleGossipedTx(payloadData: Buffer, msgId: string, source: PeerId) {
1085
+ // Per-stage timings so slow validations can be attributed to a specific stage (see A-1656).
1086
+ const stageTimings: Record<string, number> = {};
1087
+ const timed = async <T>(stage: string, fn: () => T | Promise<T>): Promise<T> => {
1088
+ const timer = new Timer();
1089
+ try {
1090
+ return await fn();
1091
+ } finally {
1092
+ const ms = timer.ms();
1093
+ stageTimings[stage] = Math.ceil(ms);
1094
+ this.instrumentation.recordTxValidationStage(stage, ms);
1095
+ }
1096
+ };
1097
+
930
1098
  const validationFunc: () => Promise<ReceivedMessageValidationResult<Tx>> = async () => {
931
- const tx = this.tryDeserialize(() => Tx.fromBuffer(payloadData), msgId, source);
1099
+ const tx = await timed('deserialize', () => this.tryDeserialize(() => Tx.fromBuffer(payloadData), msgId, source));
932
1100
  if (!tx) {
933
1101
  return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.LowToleranceError };
934
1102
  }
935
1103
 
936
- const currentBlockNumber = await this.archiver.getBlockNumber();
937
- const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
1104
+ // Stage 1 setup: chain tip, epoch info, gas fees, and L1 constants reads
1105
+ const { currentBlockNumber, firstStageValidators } = await timed('fast_validation_setup', async () => {
1106
+ const currentBlockNumber = await this.archiver.getBlockNumber();
1107
+ const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
1108
+ const firstStageValidators = await this.createFirstStageMessageValidators(
1109
+ currentBlockNumber,
1110
+ nextSlotTimestamp,
1111
+ );
1112
+ return { currentBlockNumber, firstStageValidators };
1113
+ });
938
1114
 
939
1115
  // Stage 1: fast validators (metadata, data, timestamps, double-spend, gas, phases, block header)
940
- const firstStageValidators = await this.createFirstStageMessageValidators(currentBlockNumber, nextSlotTimestamp);
941
- const firstStageOutcome = await this.runValidations(tx, firstStageValidators);
1116
+ const firstStageOutcome = await timed('fast_validation', () => this.runValidations(tx, firstStageValidators));
942
1117
  if (!firstStageOutcome.allPassed) {
943
1118
  const { name } = firstStageOutcome.failure;
944
1119
  let { severity } = firstStageOutcome.failure;
@@ -960,7 +1135,7 @@ export class LibP2PService extends WithTracer implements P2PService {
960
1135
  }
961
1136
 
962
1137
  // Pool pre-check: see if the pool would accept this tx before doing expensive proof verification
963
- const canAdd = await this.mempools.txPool.canAddPendingTx(tx);
1138
+ const canAdd = await timed('pool_precheck', () => this.mempools.txPool.canAddPendingTx(tx));
964
1139
  if (canAdd === 'ignored') {
965
1140
  this.logger.verbose(`Ignoring gossiped tx ${tx.getTxHash().toString()}: pool pre-check returned ignored`, {
966
1141
  source: source.toString(),
@@ -970,7 +1145,7 @@ export class LibP2PService extends WithTracer implements P2PService {
970
1145
 
971
1146
  // Stage 2: expensive proof verification
972
1147
  const secondStageValidators = this.createSecondStageMessageValidators();
973
- const secondStageOutcome = await this.runValidations(tx, secondStageValidators);
1148
+ const secondStageOutcome = await timed('proof_verify', () => this.runValidations(tx, secondStageValidators));
974
1149
  if (!secondStageOutcome.allPassed) {
975
1150
  const { severity, name } = secondStageOutcome.failure;
976
1151
  this.logger.verbose(`Rejecting gossiped tx ${tx.getTxHash().toString()}: stage 2 validation failed`, {
@@ -983,7 +1158,7 @@ export class LibP2PService extends WithTracer implements P2PService {
983
1158
 
984
1159
  // Pool add: persist the tx
985
1160
  const txHash = tx.getTxHash();
986
- const addResult = await this.mempools.txPool.addPendingTxs([tx], { source: 'gossip' });
1161
+ const addResult = await timed('pool_add', () => this.mempools.txPool.addPendingTxs([tx], { source: 'gossip' }));
987
1162
 
988
1163
  const wasAccepted = addResult.accepted.some(h => h.equals(txHash));
989
1164
  const wasIgnored = addResult.ignored.some(h => h.equals(txHash));
@@ -1007,7 +1182,13 @@ export class LibP2PService extends WithTracer implements P2PService {
1007
1182
  }
1008
1183
  };
1009
1184
 
1010
- const { result, obj: tx } = await this.validateReceivedMessage<Tx>(validationFunc, msgId, source, TopicType.tx);
1185
+ const { result, obj: tx } = await this.validateReceivedMessage<Tx>(
1186
+ validationFunc,
1187
+ msgId,
1188
+ source,
1189
+ TopicType.tx,
1190
+ stageTimings,
1191
+ );
1011
1192
  if (result !== TopicValidatorResult.Accept || !tx) {
1012
1193
  return;
1013
1194
  }
@@ -1129,6 +1310,7 @@ export class LibP2PService extends WithTracer implements P2PService {
1129
1310
  }
1130
1311
 
1131
1312
  // Attestation was added successfully - accept it so other nodes can also detect the equivocation
1313
+ this.checkpointAttestationCallback?.(attestation);
1132
1314
  return { result: TopicValidatorResult.Accept, obj: attestation };
1133
1315
  }
1134
1316
 
@@ -1136,20 +1318,21 @@ export class LibP2PService extends WithTracer implements P2PService {
1136
1318
  const {
1137
1319
  result,
1138
1320
  obj: block,
1139
- metadata: { isEquivocated } = {},
1140
- } = await this.validateReceivedMessage<BlockProposal, { isEquivocated: boolean }>(
1321
+ metadata: { isEquivocated, isOversized } = {},
1322
+ } = await this.validateReceivedMessage<BlockProposal, { isEquivocated: boolean; isOversized: boolean }>(
1141
1323
  () => this.validateAndStoreBlockProposal(source, BlockProposal.fromBuffer(payloadData)),
1142
1324
  msgId,
1143
1325
  source,
1144
1326
  TopicType.block_proposal,
1145
1327
  );
1146
1328
 
1147
- // If not accepted or equivocated, return
1148
- if (result !== TopicValidatorResult.Accept || !block || isEquivocated) {
1329
+ // If not accepted, equivocated, or oversized, return. Oversized proposals are re-broadcast as
1330
+ // slashing evidence but never attested or processed.
1331
+ if (result !== TopicValidatorResult.Accept || !block || isEquivocated || isOversized) {
1149
1332
  return;
1150
1333
  }
1151
1334
 
1152
- await this.processValidBlockProposal(block, source);
1335
+ await this.processValidBlockProposal(ValidatedBlockProposal(block), source);
1153
1336
  }
1154
1337
 
1155
1338
  /** Validates a block proposal. Triggers a penalization to the peer that sent it if invalid. Adds to the mempool if valid. */
@@ -1160,7 +1343,7 @@ export class LibP2PService extends WithTracer implements P2PService {
1160
1343
  protected async validateAndStoreBlockProposal(
1161
1344
  peerId: PeerId,
1162
1345
  block: BlockProposal,
1163
- ): Promise<ReceivedMessageValidationResult<BlockProposal, { isEquivocated: boolean }>> {
1346
+ ): Promise<ReceivedMessageValidationResult<BlockProposal, { isEquivocated: boolean; isOversized: boolean }>> {
1164
1347
  const validationResult = await this.blockProposalValidator.validate(block);
1165
1348
 
1166
1349
  if (validationResult.result === 'reject') {
@@ -1175,6 +1358,12 @@ export class LibP2PService extends WithTracer implements P2PService {
1175
1358
  // Try to add the proposal: this handles existence check, cap check, and adding in one call
1176
1359
  const { added, alreadyExists, count } = await this.mempools.attestationPool.tryAddBlockProposal(block);
1177
1360
  const isEquivocated = count !== undefined && count > 1;
1361
+ // An oversized proposal (index at or beyond the consensus per-checkpoint limit) is structurally valid
1362
+ // proposer misbehavior: it is stored and re-broadcast as slashing evidence but never processed or
1363
+ // attested to. No-ops when maxBlocksPerCheckpoint is unset (local/test).
1364
+ const isOversized =
1365
+ this.config.maxBlocksPerCheckpoint !== undefined &&
1366
+ block.indexWithinCheckpoint >= this.config.maxBlocksPerCheckpoint;
1178
1367
 
1179
1368
  // Duplicate proposal received, no need to re-broadcast
1180
1369
  if (alreadyExists) {
@@ -1184,7 +1373,7 @@ export class LibP2PService extends WithTracer implements P2PService {
1184
1373
  proposer: block.getSender()?.toString(),
1185
1374
  source: peerId.toString(),
1186
1375
  });
1187
- return { result: TopicValidatorResult.Ignore, obj: block, metadata: { isEquivocated } };
1376
+ return { result: TopicValidatorResult.Ignore, obj: block, metadata: { isEquivocated, isOversized } };
1188
1377
  }
1189
1378
 
1190
1379
  // Too many blocks received for this slot and index, penalize peer and do not re-broadcast
@@ -1198,11 +1387,27 @@ export class LibP2PService extends WithTracer implements P2PService {
1198
1387
  });
1199
1388
  return {
1200
1389
  result: TopicValidatorResult.Reject,
1201
- metadata: { isEquivocated },
1390
+ metadata: { isEquivocated, isOversized },
1202
1391
  severity: PeerErrorSeverity.HighToleranceError,
1203
1392
  };
1204
1393
  }
1205
1394
 
1395
+ // The proposal was stored: if oversized, invoke the oversized callback so the proposer can be
1396
+ // slashed. Fired alongside (not instead of) equivocation detection below.
1397
+ if (isOversized) {
1398
+ const proposer = block.getSender();
1399
+ if (proposer) {
1400
+ this.logger.warn(`Detected oversized block proposal at slot ${block.slotNumber}`, {
1401
+ ...block.toBlockInfo(),
1402
+ indexWithinCheckpoint: block.indexWithinCheckpoint,
1403
+ maxBlocksPerCheckpoint: this.config.maxBlocksPerCheckpoint,
1404
+ source: peerId.toString(),
1405
+ proposer: proposer.toString(),
1406
+ });
1407
+ this.oversizedProposalCallback?.({ slot: block.slotNumber, proposer });
1408
+ }
1409
+ }
1410
+
1206
1411
  // If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
1207
1412
  // and do re-broadcast it so other nodes in the network know to slash the proposer
1208
1413
  if (isEquivocated) {
@@ -1216,11 +1421,11 @@ export class LibP2PService extends WithTracer implements P2PService {
1216
1421
  if (proposer && count === 2) {
1217
1422
  this.duplicateProposalCallback?.({ slot: block.slotNumber, proposer, type: 'block' });
1218
1423
  }
1219
- return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated } };
1424
+ return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated, isOversized } };
1220
1425
  }
1221
1426
 
1222
1427
  // Otherwise, we're good to go!
1223
- return { result: TopicValidatorResult.Accept, obj: block };
1428
+ return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated: false, isOversized } };
1224
1429
  }
1225
1430
 
1226
1431
  // REFACTOR(palla): This method should be moved to the p2p_client or to a separate component,
@@ -1230,7 +1435,7 @@ export class LibP2PService extends WithTracer implements P2PService {
1230
1435
  [Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
1231
1436
  [Attributes.P2P_ID]: await block.p2pMessageLoggingIdentifier().then(i => i.toString()),
1232
1437
  }))
1233
- protected async processValidBlockProposal(block: BlockProposal, sender: PeerId) {
1438
+ protected async processValidBlockProposal(block: ValidatedBlockProposal, sender: PeerId) {
1234
1439
  const slot = block.slotNumber;
1235
1440
  this.logger.verbose(`Received block proposal for slot ${slot} from external peer ${sender.toString()}.`, {
1236
1441
  p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier(),
@@ -1241,11 +1446,29 @@ export class LibP2PService extends WithTracer implements P2PService {
1241
1446
  // Mark the txs in this proposal as protected
1242
1447
  await this.mempools.txPool.protectTxs(block.txHashes, block.blockHeader);
1243
1448
 
1244
- // Call the block received callback to validate the proposal.
1245
- // Note: Validators do NOT attest to individual blocks, only to checkpoint proposals.
1246
- const isValid = await this.blockReceivedCallback(block, sender);
1247
- if (!isValid) {
1248
- this.logger.info(`Block proposal validation failed for block ${block.blockNumber}`, block.toBlockInfo());
1449
+ if (!(await this.tryBlockReceivedCallback(block, sender))) {
1450
+ // Release the protections this proposal created so its txs return to pending. Only entries still
1451
+ // keyed to this slot are cleared, so a tx referenced by a live proposal at another slot stays protected.
1452
+ await this.mempools.txPool.unprotectTxs(block.txHashes, slot);
1453
+ }
1454
+ }
1455
+
1456
+ /**
1457
+ * Runs the block received callback to validate a proposal, and returns whether it was accepted.
1458
+ * A callback that throws is reported as a rejection: the proposal is no more usable than one explicitly
1459
+ * rejected, and letting the error escape would leave the proposal's tx protections in place.
1460
+ * Note: Validators do NOT attest to individual blocks, only to checkpoint proposals.
1461
+ */
1462
+ private async tryBlockReceivedCallback(block: ValidatedBlockProposal, sender: PeerId): Promise<boolean> {
1463
+ try {
1464
+ const isValid = await this.blockReceivedCallback(block, sender);
1465
+ if (!isValid) {
1466
+ this.logger.info(`Block proposal validation failed for block ${block.blockNumber}`, block.toBlockInfo());
1467
+ }
1468
+ return isValid;
1469
+ } catch (err) {
1470
+ this.logger.error(`Error validating block proposal for block ${block.blockNumber}`, err, block.toBlockInfo());
1471
+ return false;
1249
1472
  }
1250
1473
  }
1251
1474
 
@@ -1257,25 +1480,45 @@ export class LibP2PService extends WithTracer implements P2PService {
1257
1480
  const {
1258
1481
  result,
1259
1482
  obj: checkpoint,
1260
- metadata: { isEquivocated, processBlock } = {},
1261
- } = await this.validateReceivedMessage<CheckpointProposal, { isEquivocated: boolean; processBlock: boolean }>(
1483
+ metadata: { isEquivocated, processBlock, isOversized } = {},
1484
+ } = await this.validateReceivedMessage<
1485
+ CheckpointProposal,
1486
+ { isEquivocated: boolean; processBlock: boolean; isOversized: boolean }
1487
+ >(
1262
1488
  () => this.validateAndStoreCheckpointProposal(source, CheckpointProposal.fromBuffer(payloadData)),
1263
1489
  msgId,
1264
1490
  source,
1265
1491
  TopicType.checkpoint_proposal,
1266
1492
  );
1267
1493
 
1494
+ // An oversized checkpoint is re-broadcast as slashing evidence but never attested or processed.
1495
+ // Process checkpoint proposal if valid and neither equivocated nor oversized.
1496
+ const processCheckpointFn = () =>
1497
+ result === TopicValidatorResult.Accept && checkpoint && !isEquivocated && !isOversized
1498
+ ? this.processValidCheckpointProposal(ValidatedCheckpointProposalCore(checkpoint.toCore()), source)
1499
+ : Promise.resolve();
1500
+
1268
1501
  // If the checkpoint contained a valid last block, we process it even if the checkpoint itself is to be rejected
1269
1502
  // TODO(palla/mbps): Is this ok? Should we be considering a block from a checkpoint that was equivocated?
1270
- if (processBlock && checkpoint?.getBlockProposal()) {
1271
- await this.processValidBlockProposal(checkpoint.getBlockProposal()!, source);
1272
- }
1273
-
1274
- if (result !== TopicValidatorResult.Accept || !checkpoint || isEquivocated) {
1503
+ const processBlockFn = () =>
1504
+ processBlock && checkpoint && checkpoint.getBlockProposal()
1505
+ ? this.processValidBlockProposal(ValidatedBlockProposal(checkpoint.getBlockProposal()!), source)
1506
+ : Promise.resolve();
1507
+
1508
+ // A node that skips checkpoint validation attests without re-executing the embedded last block, so run
1509
+ // the checkpoint callback first: this creates and broadcasts the attestation before the block is
1510
+ // processed. Otherwise the block's re-execution — which can stall until the re-execution deadline
1511
+ // waiting for a parent that may never arrive — would delay the attestation past the slot's attestation
1512
+ // window, after which peers reject it as stale.
1513
+ if (this.config.skipCheckpointProposalValidation) {
1514
+ await processCheckpointFn();
1515
+ await processBlockFn();
1275
1516
  return;
1276
1517
  }
1277
1518
 
1278
- await this.processValidCheckpointProposal(checkpoint.toCore(), source);
1519
+ // Process the block first, since it's required for the checkpoint proposal validation.
1520
+ await processBlockFn();
1521
+ await processCheckpointFn();
1279
1522
  }
1280
1523
 
1281
1524
  /**
@@ -1288,7 +1531,12 @@ export class LibP2PService extends WithTracer implements P2PService {
1288
1531
  protected async validateAndStoreCheckpointProposal(
1289
1532
  peerId: PeerId,
1290
1533
  checkpoint: CheckpointProposal,
1291
- ): Promise<ReceivedMessageValidationResult<CheckpointProposal, { isEquivocated: boolean; processBlock: boolean }>> {
1534
+ ): Promise<
1535
+ ReceivedMessageValidationResult<
1536
+ CheckpointProposal,
1537
+ { isEquivocated: boolean; processBlock: boolean; isOversized: boolean }
1538
+ >
1539
+ > {
1292
1540
  const validationResult = await this.checkpointProposalValidator.validate(checkpoint);
1293
1541
 
1294
1542
  if (validationResult.result === 'reject') {
@@ -1303,13 +1551,16 @@ export class LibP2PService extends WithTracer implements P2PService {
1303
1551
  // Extract and try to add the block proposal first if present
1304
1552
  const blockProposal = checkpoint.getBlockProposal();
1305
1553
  let processBlock = false;
1554
+ let isOversized = false;
1306
1555
  if (blockProposal) {
1307
1556
  this.logger.debug(`Validating block proposal from propagated checkpoint`, {
1308
1557
  [Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
1309
1558
  [Attributes.P2P_ID]: peerId.toString(),
1310
1559
  });
1311
1560
  const blockProposalResult = await this.validateAndStoreBlockProposal(peerId, blockProposal);
1312
- const { obj, metadata: { isEquivocated } = {} } = blockProposalResult;
1561
+ const { obj, metadata: { isEquivocated, isOversized: blockIsOversized } = {} } = blockProposalResult;
1562
+ isOversized = blockIsOversized ?? false;
1563
+
1313
1564
  if (blockProposalResult.result === TopicValidatorResult.Reject || !obj || isEquivocated) {
1314
1565
  this.logger.debug(`Rejecting checkpoint due to invalid last block proposal`, {
1315
1566
  [Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
@@ -1322,7 +1573,8 @@ export class LibP2PService extends WithTracer implements P2PService {
1322
1573
  severity:
1323
1574
  'severity' in blockProposalResult ? blockProposalResult.severity : PeerErrorSeverity.MidToleranceError,
1324
1575
  };
1325
- } else if (blockProposalResult.result === TopicValidatorResult.Accept && obj && !isEquivocated) {
1576
+ } else if (blockProposalResult.result === TopicValidatorResult.Accept && obj && !isEquivocated && !isOversized) {
1577
+ // An oversized terminal block is re-broadcast as slashing evidence but never processed.
1326
1578
  processBlock = true;
1327
1579
  }
1328
1580
  }
@@ -1342,11 +1594,11 @@ export class LibP2PService extends WithTracer implements P2PService {
1342
1594
  return {
1343
1595
  result: TopicValidatorResult.Ignore,
1344
1596
  obj: checkpoint,
1345
- metadata: { isEquivocated, processBlock },
1597
+ metadata: { isEquivocated, processBlock, isOversized },
1346
1598
  };
1347
1599
  }
1348
1600
 
1349
- // Too many checkpoint proposals received for this slot, penalize peer and do not re-broadcast
1601
+ // Too many checkpoint proposals received for this slot, penalize peer and do not re-broadcast.
1350
1602
  // Note: We still return the checkpoint obj so the lastBlock can be processed if valid
1351
1603
  if (!added) {
1352
1604
  this.logger.warn(`Penalizing peer for checkpoint proposal exceeding per-slot cap`, {
@@ -1357,7 +1609,7 @@ export class LibP2PService extends WithTracer implements P2PService {
1357
1609
  return {
1358
1610
  result: TopicValidatorResult.Reject,
1359
1611
  obj: checkpoint,
1360
- metadata: { isEquivocated, processBlock },
1612
+ metadata: { isEquivocated, processBlock, isOversized },
1361
1613
  severity: PeerErrorSeverity.HighToleranceError,
1362
1614
  };
1363
1615
  }
@@ -1378,12 +1630,16 @@ export class LibP2PService extends WithTracer implements P2PService {
1378
1630
  return {
1379
1631
  result: TopicValidatorResult.Accept,
1380
1632
  obj: checkpoint,
1381
- metadata: { isEquivocated, processBlock },
1633
+ metadata: { isEquivocated, processBlock, isOversized },
1382
1634
  };
1383
1635
  }
1384
1636
 
1385
1637
  // Otherwise, we're good to go!
1386
- return { result: TopicValidatorResult.Accept, obj: checkpoint, metadata: { processBlock, isEquivocated } };
1638
+ return {
1639
+ result: TopicValidatorResult.Accept,
1640
+ obj: checkpoint,
1641
+ metadata: { processBlock, isEquivocated, isOversized },
1642
+ };
1387
1643
  }
1388
1644
 
1389
1645
  /**
@@ -1395,7 +1651,7 @@ export class LibP2PService extends WithTracer implements P2PService {
1395
1651
  [Attributes.BLOCK_ARCHIVE]: checkpoint.archive.toString(),
1396
1652
  [Attributes.P2P_ID]: await checkpoint.p2pMessageLoggingIdentifier().then(i => i.toString()),
1397
1653
  }))
1398
- protected async processValidCheckpointProposal(checkpoint: CheckpointProposalCore, sender: PeerId) {
1654
+ protected async processValidCheckpointProposal(checkpoint: ValidatedCheckpointProposalCore, sender: PeerId) {
1399
1655
  const slot = checkpoint.slotNumber;
1400
1656
  this.logger.verbose(`Received checkpoint proposal for slot ${slot} from external peer ${sender.toString()}.`, {
1401
1657
  p2pMessageIdentifier: await checkpoint.p2pMessageLoggingIdentifier(),
@@ -1404,9 +1660,11 @@ export class LibP2PService extends WithTracer implements P2PService {
1404
1660
  source: sender.toString(),
1405
1661
  });
1406
1662
 
1663
+ await this.allNodesCheckpointReceivedCallback(checkpoint, sender);
1664
+
1407
1665
  // Call the checkpoint received callback with the core version (without lastBlock)
1408
1666
  // to validate and potentially generate attestations
1409
- const attestations = await this.checkpointReceivedCallback(checkpoint, sender);
1667
+ const attestations = await this.validatorCheckpointReceivedCallback(checkpoint, sender);
1410
1668
  if (attestations && attestations.length > 0) {
1411
1669
  // If the callback returned attestations, add them to the pool and propagate them
1412
1670
  await this.mempools.attestationPool.addOwnCheckpointAttestations(attestations);
@@ -1429,82 +1687,87 @@ export class LibP2PService extends WithTracer implements P2PService {
1429
1687
  }
1430
1688
 
1431
1689
  /**
1432
- * Validate the requested block transactions. Allow partial returns.
1690
+ * Validate the requested block transactions request-response consistency.
1691
+ * It does NOT validate the transactions themselves.
1433
1692
  * @param request - The block transactions request.
1434
1693
  * @param response - The block transactions response.
1435
1694
  * @param peerId - The ID of the peer that made the request.
1436
- * @returns True if the requested block transactions are valid, false otherwise.
1695
+ * @returns True if the request-response is consistent, false otherwise.
1437
1696
  */
1438
- @trackSpan('Libp2pService.validateRequestedBlockTxs', request => ({
1697
+ @trackSpan('Libp2pService.validateRequestedBlockTxsConsistency', request => ({
1439
1698
  [Attributes.BLOCK_ARCHIVE]: request.archiveRoot.toString(),
1440
1699
  }))
1441
- protected async validateRequestedBlockTxs(
1700
+ protected async validateRequestedBlockTxsConsistency(
1442
1701
  request: BlockTxsRequest,
1443
1702
  response: BlockTxsResponse,
1444
1703
  peerId: PeerId,
1445
1704
  ): Promise<boolean> {
1446
- const requestedTxValidator = this.createRequestedTxValidator();
1447
-
1448
1705
  try {
1449
- if (!response.archiveRoot.equals(request.archiveRoot)) {
1706
+ // Check for duplicates txs (or hashes) in the response.
1707
+ const returnedHashes = response.txs.map(tx => tx.getTxHash().toString());
1708
+ const uniqueReturned = new Set(returnedHashes.map(h => h.toString()));
1709
+ if (uniqueReturned.size !== returnedHashes.length) {
1450
1710
  this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1451
- throw new ValidationError(
1452
- `Received block txs for unexpected archive root: expected ${request.archiveRoot.toString()}, got ${response.archiveRoot.toString()}`,
1711
+ throw new ValidationError(`Received duplicate txs in block txs response`);
1712
+ }
1713
+
1714
+ // We get the block tx hashes from the proposal or the archiver.
1715
+ const proposal = await this.mempools.attestationPool.getBlockProposalByArchive(request.archiveRoot.toString());
1716
+ const blockTxHashes =
1717
+ proposal?.txHashes ??
1718
+ (await this.archiver.getBlock({ archive: request.archiveRoot }))?.body.txEffects.map(e => e.txHash);
1719
+
1720
+ // If we don't have the block, we can't verify membership/order of the returned txs.
1721
+ if (!blockTxHashes) {
1722
+ // This shouldn't happen, since we asked for the block (proposal).
1723
+ // Neither a local proposal nor an archived block: we cannot verify membership/order of the
1724
+ // returned txs. This is a local-state gap, not a peer fault, so we do not penalize.
1725
+ this.logger.warn(
1726
+ `Block ${request.archiveRoot.toString()} not found in attestation pool or archiver; cannot validate membership/order of returned txs`,
1453
1727
  );
1728
+ // NOTE: We mark the response as invalid, given the limitations of a true/false return value.
1729
+ return false;
1454
1730
  }
1455
1731
 
1456
- if (response.txIndices.getLength() !== request.txIndices.getLength()) {
1457
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1732
+ // Verify that the returned tx hashes are a subset of (tx hashes by index) U (explicitly requested tx hashes).
1733
+ const uniqueRequestedHashes = new Set([
1734
+ // tx hashes requested by index.
1735
+ ...request.txIndices.getTrueIndices().map(i => blockTxHashes[i].toString()),
1736
+ // explicitly requested tx hashes.
1737
+ ...request.txHashes.map(h => h.toString()),
1738
+ ]);
1739
+ if (!returnedHashes.every(h => uniqueRequestedHashes.has(h))) {
1740
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1458
1741
  throw new ValidationError(
1459
- `Received block txs with mismatched bitvector length: expected ${request.txIndices.getLength()}, got ${response.txIndices.getLength()}`,
1742
+ 'Returned txs should be a subset of (tx hashes by index) U (explicitly requested tx hashes)',
1460
1743
  );
1461
1744
  }
1462
1745
 
1463
- // Check no duplicates and not exceeding returnable count
1464
- const requestedIndices = new Set(request.txIndices.getTrueIndices());
1465
- const availableIndices = new Set(response.txIndices.getTrueIndices());
1466
- const maxReturnable = [...requestedIndices].filter(i => availableIndices.has(i)).length;
1467
-
1468
- const returnedHashes = await Promise.all(response.txs.map(tx => tx.getTxHash().toString()));
1469
- const uniqueReturned = new Set(returnedHashes.map(h => h.toString()));
1470
- if (uniqueReturned.size !== returnedHashes.length) {
1471
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1472
- throw new ValidationError(`Received duplicate txs in block txs response`);
1746
+ // Tx indices information is optional, but if present, we need to verify a few things.
1747
+ if (!response.peerHasBlock()) {
1748
+ this.logger.debug(`Peer ${peerId.toString()} signalled missing block`);
1749
+ return true;
1473
1750
  }
1474
- if (response.txs.length > maxReturnable) {
1751
+
1752
+ // If the response has a non-empty bitvector, it needs to be consistent with the number of txs in the block.
1753
+ if (response.txIndices.getLength() !== blockTxHashes.length) {
1475
1754
  this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1476
1755
  throw new ValidationError(
1477
- `Received more txs (${response.txs.length}) than requested-and-available (${maxReturnable})`,
1756
+ `Received block txs with mismatched bitvector length: expected ${blockTxHashes.length}, got ${response.txIndices.getLength()}`,
1478
1757
  );
1479
1758
  }
1480
1759
 
1481
- // Given proposal (should have locally), ensure returned txs are valid subset and match request indices
1482
- const proposal = await this.mempools.attestationPool.getBlockProposal(request.archiveRoot.toString());
1483
- if (proposal) {
1484
- // Build intersected indices
1485
- const intersectIdx = request.txIndices.getTrueIndices().filter(i => response.txIndices.isSet(i));
1486
-
1487
- // Enforce subset membership and preserve increasing order by index.
1488
- const hashToIndexInProposal = new Map<string, number>(
1489
- proposal.txHashes.map((h, i) => [h.toString(), i] as [string, number]),
1490
- );
1491
- const allowedIndexSet = new Set(intersectIdx);
1492
- const indices = returnedHashes.map(h => hashToIndexInProposal.get(h));
1493
- const allAllowed = indices.every(idx => idx !== undefined && allowedIndexSet.has(idx));
1494
- const strictlyIncreasing = indices.every((idx, i) => (i === 0 ? idx !== undefined : idx! > indices[i - 1]!));
1495
- if (!allAllowed || !strictlyIncreasing) {
1496
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1497
- throw new ValidationError('Returned txs do not match expected subset/order for requested indices');
1498
- }
1499
- } else {
1500
- // No local proposal, cannot check the membership/order of the returned txs
1501
- this.logger.warn(
1502
- `Block proposal not found for archive root ${request.archiveRoot.toString()}; cannot validate membership/order of returned txs`,
1503
- );
1504
- return false;
1760
+ // For every index we requested that the peer claims to have (per its bitvector), the
1761
+ // corresponding tx must be present in the response. A peer advertising availability but
1762
+ // withholding the tx is inconsistent.
1763
+ const returnedHashSet = new Set(returnedHashes);
1764
+ const expectedIndices = request.txIndices.getTrueIndices().filter(i => response.txIndices.isSet(i));
1765
+ const missingExpected = expectedIndices.some(i => !returnedHashSet.has(blockTxHashes[i].toString()));
1766
+ if (missingExpected) {
1767
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1768
+ throw new ValidationError('Peer advertised requested txs via indices but did not return them');
1505
1769
  }
1506
1770
 
1507
- await Promise.all(response.txs.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator)));
1508
1771
  return true;
1509
1772
  } catch (e: any) {
1510
1773
  if (e instanceof ValidationError) {
@@ -1517,125 +1780,8 @@ export class LibP2PService extends WithTracer implements P2PService {
1517
1780
  }
1518
1781
  }
1519
1782
 
1520
- /**
1521
- * Validate a collection of txs that has been requested from a peer.
1522
- *
1523
- * The core component of this validator is that each tx hash MUST match the requested tx hash,
1524
- * In order to perform this check, the tx proof must be verified.
1525
- *
1526
- * Note: This function is called from within `ReqResp.sendRequest` as part of the
1527
- * ReqRespSubProtocol.TX subprotocol validation.
1528
- *
1529
- * @param requestedTxHash - The collection of the txs that was requested.
1530
- * @param responseTx - The collection of txs that was received as a response to the request.
1531
- * @param peerId - The peer ID of the peer that sent the tx.
1532
- * @returns True if the whole collection of txs is valid, false otherwise.
1533
- */
1534
- @trackSpan('Libp2pService.validateRequestedTx', (requestedTxHash, _responseTx) => ({
1535
- [Attributes.TX_HASH]: requestedTxHash.toString(),
1536
- }))
1537
- private async validateRequestedTxs(requestedTxHash: TxHash[], responseTx: Tx[], peerId: PeerId): Promise<boolean> {
1538
- const requested = new Set(requestedTxHash.map(h => h.toString()));
1539
- const requestedTxValidator = this.createRequestedTxValidator();
1540
-
1541
- //TODO: (mralj) - this is somewhat naive implementation, if single tx is invalid we consider the whole response invalid.
1542
- // I think we should still extract the valid txs and return them, so that we can still use the response.
1543
- try {
1544
- await Promise.all(responseTx.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator, requested)));
1545
- return true;
1546
- } catch (e: any) {
1547
- if (e instanceof ValidationError) {
1548
- this.logger.warn(`Failed to validate requested txs from peer ${peerId.toString()}, reason ${e.message}`);
1549
- } else {
1550
- this.logger.error(`Error during validation of requested txs`, e);
1551
- }
1552
-
1553
- return false;
1554
- }
1555
- }
1556
-
1557
- /**
1558
- * Validates a BLOCK response.
1559
- *
1560
- * If a local copy exists, enforces hash equality. If missing, rejects (no penalty) since the hash cannot be verified.
1561
- * Penalizes on block number mismatch or hash mismatch.
1562
- *
1563
- * @param requestedBlockNumber - The requested block number.
1564
- * @param responseBlock - The block returned by the peer.
1565
- * @param peerId - The peer that returned the block.
1566
- * @returns True if the response is valid, false otherwise.
1567
- */
1568
- @trackSpan('Libp2pService.validateRequestedBlock', (requestedBlockNumber, _responseBlock) => ({
1569
- [Attributes.BLOCK_NUMBER]: requestedBlockNumber.toString(),
1570
- }))
1571
- protected async validateRequestedBlock(
1572
- requestedBlockNumber: Fr,
1573
- responseBlock: L2Block,
1574
- peerId: PeerId,
1575
- ): Promise<boolean> {
1576
- try {
1577
- const reqNum = Number(requestedBlockNumber.toString());
1578
- if (responseBlock.number !== reqNum) {
1579
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1580
- return false;
1581
- }
1582
-
1583
- const local = await this.archiver.getBlock(BlockNumber(reqNum));
1584
- if (!local) {
1585
- // We are missing the local block; we cannot verify the hash yet. Reject without penalizing.
1586
- // TODO: Consider extending this validator to accept an expected hash or
1587
- // performing quorum-based checks when using P2P syncing prior to L1 sync.
1588
- this.logger.warn(`Local block ${reqNum} not found; rejecting BLOCK response without hash verification`);
1589
- return false;
1590
- }
1591
- const [localHash, respHash] = await Promise.all([local.hash(), responseBlock.hash()]);
1592
- if (!localHash.equals(respHash)) {
1593
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1594
- return false;
1595
- }
1596
-
1597
- return true;
1598
- } catch (e) {
1599
- this.logger.warn(`Error validating requested block`, e);
1600
- return false;
1601
- }
1602
- }
1603
-
1604
- protected async validateRequestedTx(
1605
- tx: Tx,
1606
- peerId: PeerId,
1607
- txValidator: TxValidator,
1608
- requested?: Set<`0x${string}`>,
1609
- ) {
1610
- const penalize = (severity: PeerErrorSeverity) => this.peerManager.penalizePeer(peerId, severity);
1611
- if (requested && !requested.has(tx.getTxHash().toString())) {
1612
- penalize(PeerErrorSeverity.MidToleranceError);
1613
- throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that was not requested.`);
1614
- }
1615
-
1616
- const { result } = await txValidator.validateTx(tx);
1617
- if (result === 'invalid') {
1618
- penalize(PeerErrorSeverity.LowToleranceError);
1619
- throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that is invalid.`);
1620
- }
1621
- }
1622
-
1623
- protected createRequestedTxValidator(): TxValidator {
1624
- return createTxValidatorForReqResponseReceivedTxs(this.proofVerifier, {
1625
- l1ChainId: this.config.l1ChainId,
1626
- rollupVersion: this.config.rollupVersion,
1627
- });
1628
- }
1629
-
1630
- private async getGasFees(blockNumber: BlockNumber): Promise<GasFees> {
1631
- if (blockNumber === this.feesCache?.blockNumber) {
1632
- return this.feesCache.gasFees;
1633
- }
1634
-
1635
- const header = await this.archiver.getBlockHeader(blockNumber);
1636
- const gasFees = header?.globalVariables.gasFees ?? GasFees.empty();
1637
- this.feesCache = { blockNumber, gasFees };
1638
- return gasFees;
1783
+ private getGasFees(): Promise<GasFees> {
1784
+ return this.blockMinFeesProvider.getCurrentMinFees();
1639
1785
  }
1640
1786
 
1641
1787
  /**
@@ -1649,8 +1795,10 @@ export class LibP2PService extends WithTracer implements P2PService {
1649
1795
  l1ChainId: this.config.l1ChainId,
1650
1796
  rollupVersion: this.config.rollupVersion,
1651
1797
  proofVerifier: this.proofVerifier,
1798
+ txValidationCache: this.txValidationCache,
1652
1799
  },
1653
1800
  peerScoring: this.peerManager,
1801
+ validateRequestedBlockTxsConsistency: this.validateRequestedBlockTxsConsistency.bind(this),
1654
1802
  };
1655
1803
  }
1656
1804
 
@@ -1659,16 +1807,19 @@ export class LibP2PService extends WithTracer implements P2PService {
1659
1807
  this.proofVerifier,
1660
1808
  { l1ChainId: this.config.l1ChainId, rollupVersion: this.config.rollupVersion },
1661
1809
  this.logger.getBindings(),
1810
+ this.txValidationCache,
1662
1811
  );
1663
1812
 
1664
1813
  const results = await Promise.all(
1665
- txs.map(async tx => {
1666
- const result = await validator.validateTx(tx);
1667
- return result.result !== 'invalid';
1668
- }),
1814
+ txs.map(async tx => ({ txHash: tx.getTxHash(), result: await validator.validateTx(tx) })),
1669
1815
  );
1670
- if (results.some(value => value === false)) {
1671
- throw new Error('Invalid tx detected');
1816
+
1817
+ const invalidTxs = results.flatMap(({ txHash, result }) =>
1818
+ result.result === 'invalid' ? [{ txHash, reasons: result.reason }] : [],
1819
+ );
1820
+
1821
+ if (invalidTxs.length > 0) {
1822
+ throw new InvalidBlockProposalTxsError(invalidTxs);
1672
1823
  }
1673
1824
  }
1674
1825
 
@@ -1677,13 +1828,14 @@ export class LibP2PService extends WithTracer implements P2PService {
1677
1828
  currentBlockNumber: BlockNumber,
1678
1829
  nextSlotTimestamp: UInt64,
1679
1830
  ): Promise<Record<string, TransactionValidator>> {
1680
- const gasFees = await this.getGasFees(currentBlockNumber);
1831
+ const gasFees = await this.getGasFees();
1681
1832
  const allowedInSetup = [
1682
1833
  ...(await getDefaultAllowedSetupFunctions()),
1683
1834
  ...(this.config.txPublicSetupAllowListExtend ?? []),
1684
1835
  ];
1685
1836
  const blockNumber = BlockNumber(currentBlockNumber + 1);
1686
1837
  const l1Constants = await this.archiver.getL1Constants();
1838
+ const networkTxGasLimits = getNetworkTxGasLimits(this.config, l1Constants);
1687
1839
 
1688
1840
  return createFirstStageTxValidationsForGossipedTransactions(
1689
1841
  nextSlotTimestamp,
@@ -1698,9 +1850,8 @@ export class LibP2PService extends WithTracer implements P2PService {
1698
1850
  allowedInSetup,
1699
1851
  this.logger.getBindings(),
1700
1852
  {
1701
- rollupManaLimit: l1Constants.rollupManaLimit,
1702
- maxBlockL2Gas: this.config.validateMaxL2BlockGas,
1703
- maxBlockDAGas: this.config.validateMaxDABlockGas,
1853
+ maxTxL2Gas: networkTxGasLimits.l2Gas,
1854
+ maxTxDAGas: networkTxGasLimits.daGas,
1704
1855
  },
1705
1856
  );
1706
1857
  }