@aztec/p2p 0.0.1-commit.42ee6df9b → 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 (360) 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 +32 -23
  8. package/dest/client/interface.d.ts +44 -11
  9. package/dest/client/interface.d.ts.map +1 -1
  10. package/dest/client/p2p_client.d.ts +34 -19
  11. package/dest/client/p2p_client.d.ts.map +1 -1
  12. package/dest/client/p2p_client.js +119 -59
  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/reqresp.error.d.ts +9 -16
  17. package/dest/errors/reqresp.error.d.ts.map +1 -1
  18. package/dest/errors/reqresp.error.js +10 -17
  19. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +98 -56
  20. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  21. package/dest/mem_pools/attestation_pool/attestation_pool.js +270 -198
  22. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +1 -1
  23. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  24. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +218 -59
  25. package/dest/mem_pools/attestation_pool/mocks.d.ts +1 -1
  26. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  27. package/dest/mem_pools/attestation_pool/mocks.js +6 -4
  28. package/dest/mem_pools/instrumentation.d.ts +9 -4
  29. package/dest/mem_pools/instrumentation.d.ts.map +1 -1
  30. package/dest/mem_pools/instrumentation.js +28 -9
  31. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts +10 -5
  32. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts.map +1 -1
  33. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.js +29 -20
  34. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +3 -3
  35. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -1
  36. package/dest/mem_pools/tx_pool_v2/deleted_pool.js +6 -6
  37. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts +1 -1
  38. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -1
  39. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +3 -3
  40. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts +1 -1
  41. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts.map +1 -1
  42. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.js +3 -1
  43. package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts +2 -1
  44. package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts.map +1 -1
  45. package/dest/mem_pools/tx_pool_v2/eviction/index.js +1 -0
  46. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts +16 -0
  47. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts.map +1 -0
  48. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.js +64 -0
  49. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts +1 -1
  50. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts.map +1 -1
  51. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.js +3 -4
  52. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts +1 -1
  53. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts.map +1 -1
  54. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +6 -7
  55. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts +1 -1
  56. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts.map +1 -1
  57. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.js +6 -5
  58. package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts +11 -1
  59. package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts.map +1 -1
  60. package/dest/mem_pools/tx_pool_v2/instrumentation.js +24 -0
  61. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +33 -5
  62. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
  63. package/dest/mem_pools/tx_pool_v2/interfaces.js +2 -1
  64. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +5 -2
  65. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
  66. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +8 -5
  67. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +15 -1
  68. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -1
  69. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +31 -0
  70. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +16 -3
  71. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
  72. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +83 -32
  73. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +50 -6
  74. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
  75. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +243 -127
  76. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +10 -3
  77. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
  78. package/dest/msg_validators/attestation_validator/attestation_validator.js +38 -16
  79. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +7 -3
  80. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
  81. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +4 -5
  82. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +8 -3
  83. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
  84. package/dest/msg_validators/proposal_validator/block_proposal_validator.js +2 -2
  85. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +8 -3
  86. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
  87. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +8 -2
  88. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +25 -3
  89. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
  90. package/dest/msg_validators/proposal_validator/proposal_validator.js +85 -15
  91. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +1 -1
  92. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
  93. package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +8 -15
  94. package/dest/msg_validators/tx_validator/allowed_public_setup.js +3 -3
  95. package/dest/msg_validators/tx_validator/archive_cache.js +1 -1
  96. package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts +15 -0
  97. package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts.map +1 -0
  98. package/dest/msg_validators/tx_validator/cached_tx_validator.js +19 -0
  99. package/dest/msg_validators/tx_validator/data_validator.d.ts +2 -1
  100. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
  101. package/dest/msg_validators/tx_validator/data_validator.js +1 -0
  102. package/dest/msg_validators/tx_validator/factory.d.ts +14 -15
  103. package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
  104. package/dest/msg_validators/tx_validator/factory.js +29 -16
  105. package/dest/msg_validators/tx_validator/gas_validator.d.ts +47 -10
  106. package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
  107. package/dest/msg_validators/tx_validator/gas_validator.js +65 -48
  108. package/dest/msg_validators/tx_validator/index.d.ts +3 -1
  109. package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
  110. package/dest/msg_validators/tx_validator/index.js +2 -0
  111. package/dest/msg_validators/tx_validator/phases_validator.js +2 -2
  112. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +2 -1
  113. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -1
  114. package/dest/msg_validators/tx_validator/tx_proof_validator.js +2 -0
  115. package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts +48 -0
  116. package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts.map +1 -0
  117. package/dest/msg_validators/tx_validator/tx_validation_cache.js +68 -0
  118. package/dest/services/data_store.d.ts +1 -1
  119. package/dest/services/data_store.d.ts.map +1 -1
  120. package/dest/services/data_store.js +5 -5
  121. package/dest/services/discv5/discV5_service.d.ts +13 -2
  122. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  123. package/dest/services/discv5/discV5_service.js +119 -23
  124. package/dest/services/discv5/persisted_enr_store.d.ts +38 -0
  125. package/dest/services/discv5/persisted_enr_store.d.ts.map +1 -0
  126. package/dest/services/discv5/persisted_enr_store.js +145 -0
  127. package/dest/services/dummy_service.d.ts +18 -18
  128. package/dest/services/dummy_service.d.ts.map +1 -1
  129. package/dest/services/dummy_service.js +19 -18
  130. package/dest/services/encoding.d.ts +19 -6
  131. package/dest/services/encoding.d.ts.map +1 -1
  132. package/dest/services/encoding.js +25 -15
  133. package/dest/services/gossipsub/topic_score_params.d.ts +6 -12
  134. package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -1
  135. package/dest/services/gossipsub/topic_score_params.js +4 -14
  136. package/dest/services/libp2p/instrumentation.d.ts +6 -1
  137. package/dest/services/libp2p/instrumentation.d.ts.map +1 -1
  138. package/dest/services/libp2p/instrumentation.js +21 -0
  139. package/dest/services/libp2p/libp2p_service.d.ts +40 -43
  140. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  141. package/dest/services/libp2p/libp2p_service.js +370 -276
  142. package/dest/services/peer-manager/peer_manager.d.ts +26 -2
  143. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  144. package/dest/services/peer-manager/peer_manager.js +103 -17
  145. package/dest/services/peer-manager/peer_scoring.d.ts +30 -1
  146. package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
  147. package/dest/services/peer-manager/peer_scoring.js +74 -3
  148. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +1 -8
  149. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
  150. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +35 -44
  151. package/dest/services/reqresp/batch-tx-requester/interface.d.ts +8 -5
  152. package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -1
  153. package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts +5 -14
  154. package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts.map +1 -1
  155. package/dest/services/reqresp/batch-tx-requester/tx_validator.js +6 -20
  156. package/dest/services/reqresp/config.d.ts +15 -3
  157. package/dest/services/reqresp/config.d.ts.map +1 -1
  158. package/dest/services/reqresp/config.js +11 -0
  159. package/dest/services/reqresp/interface.d.ts +16 -18
  160. package/dest/services/reqresp/interface.d.ts.map +1 -1
  161. package/dest/services/reqresp/interface.js +10 -20
  162. package/dest/services/reqresp/metrics.d.ts +1 -1
  163. package/dest/services/reqresp/metrics.d.ts.map +1 -1
  164. package/dest/services/reqresp/metrics.js +0 -1
  165. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +4 -3
  166. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -1
  167. package/dest/services/reqresp/protocols/block_txs/bitvector.js +3 -2
  168. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +1 -1
  169. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
  170. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +31 -28
  171. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +12 -7
  172. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
  173. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +42 -24
  174. package/dest/services/reqresp/protocols/index.d.ts +1 -2
  175. package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
  176. package/dest/services/reqresp/protocols/index.js +0 -1
  177. package/dest/services/reqresp/protocols/tx.d.ts +1 -1
  178. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
  179. package/dest/services/reqresp/protocols/tx.js +1 -3
  180. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
  181. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
  182. package/dest/services/reqresp/rate-limiter/rate_limits.js +3 -13
  183. package/dest/services/reqresp/reqresp.d.ts +7 -29
  184. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  185. package/dest/services/reqresp/reqresp.js +59 -209
  186. package/dest/services/service.d.ts +41 -19
  187. package/dest/services/service.d.ts.map +1 -1
  188. package/dest/services/tx_collection/config.d.ts +2 -23
  189. package/dest/services/tx_collection/config.d.ts.map +1 -1
  190. package/dest/services/tx_collection/config.js +2 -55
  191. package/dest/services/tx_collection/file_store_tx_collection.d.ts +12 -28
  192. package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -1
  193. package/dest/services/tx_collection/file_store_tx_collection.js +43 -83
  194. package/dest/services/tx_collection/index.d.ts +2 -3
  195. package/dest/services/tx_collection/index.d.ts.map +1 -1
  196. package/dest/services/tx_collection/index.js +0 -1
  197. package/dest/services/tx_collection/instrumentation.d.ts +1 -1
  198. package/dest/services/tx_collection/instrumentation.d.ts.map +1 -1
  199. package/dest/services/tx_collection/instrumentation.js +0 -2
  200. package/dest/services/tx_collection/tx_collection.d.ts +35 -51
  201. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
  202. package/dest/services/tx_collection/tx_collection.js +275 -119
  203. package/dest/services/tx_collection/tx_collection_sink.d.ts +1 -1
  204. package/dest/services/tx_collection/tx_collection_sink.js +2 -2
  205. package/dest/services/tx_collection/tx_source.d.ts +1 -1
  206. package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
  207. package/dest/services/tx_collection/tx_source.js +4 -1
  208. package/dest/services/tx_file_store/tx_file_store.d.ts +1 -3
  209. package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -1
  210. package/dest/services/tx_file_store/tx_file_store.js +4 -14
  211. package/dest/services/tx_provider.d.ts +3 -1
  212. package/dest/services/tx_provider.d.ts.map +1 -1
  213. package/dest/services/tx_provider.js +3 -0
  214. package/dest/test-helpers/make-test-p2p-clients.d.ts +1 -1
  215. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  216. package/dest/test-helpers/make-test-p2p-clients.js +5 -2
  217. package/dest/test-helpers/mock-pubsub.d.ts +33 -9
  218. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  219. package/dest/test-helpers/mock-pubsub.js +57 -44
  220. package/dest/test-helpers/reqresp-nodes.d.ts +4 -5
  221. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  222. package/dest/test-helpers/reqresp-nodes.js +16 -18
  223. package/dest/test-helpers/test_tx_provider.d.ts +3 -1
  224. package/dest/test-helpers/test_tx_provider.d.ts.map +1 -1
  225. package/dest/test-helpers/test_tx_provider.js +3 -0
  226. package/dest/test-helpers/testbench-utils.d.ts +15 -14
  227. package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
  228. package/dest/test-helpers/testbench-utils.js +28 -13
  229. package/dest/testbench/p2p_client_testbench_worker.d.ts +3 -5
  230. package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -1
  231. package/dest/testbench/p2p_client_testbench_worker.js +60 -27
  232. package/dest/testbench/worker_client_manager.d.ts +10 -6
  233. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  234. package/dest/testbench/worker_client_manager.js +51 -8
  235. package/dest/util.d.ts +11 -6
  236. package/dest/util.d.ts.map +1 -1
  237. package/dest/util.js +35 -14
  238. package/dest/versioning.d.ts +3 -6
  239. package/dest/versioning.d.ts.map +1 -1
  240. package/dest/versioning.js +3 -24
  241. package/package.json +17 -17
  242. package/src/bootstrap/bootstrap.ts +17 -3
  243. package/src/client/factory.ts +48 -24
  244. package/src/client/interface.ts +39 -17
  245. package/src/client/p2p_client.ts +135 -92
  246. package/src/client/test/{tx_proposal_collector/README.md → p2p_client.batch_tx_requester.bench.README.md} +23 -53
  247. package/src/config.ts +159 -34
  248. package/src/errors/reqresp.error.ts +13 -19
  249. package/src/mem_pools/attestation_pool/attestation_pool.ts +325 -242
  250. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +238 -63
  251. package/src/mem_pools/attestation_pool/mocks.ts +13 -8
  252. package/src/mem_pools/instrumentation.ts +18 -12
  253. package/src/mem_pools/tx_pool_v2/archive/tx_archive.ts +30 -20
  254. package/src/mem_pools/tx_pool_v2/deleted_pool.ts +7 -7
  255. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +2 -4
  256. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +3 -2
  257. package/src/mem_pools/tx_pool_v2/eviction/index.ts +1 -0
  258. package/src/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.ts +67 -0
  259. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.ts +1 -3
  260. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +5 -7
  261. package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +5 -6
  262. package/src/mem_pools/tx_pool_v2/instrumentation.ts +27 -0
  263. package/src/mem_pools/tx_pool_v2/interfaces.ts +31 -4
  264. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +13 -7
  265. package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +35 -0
  266. package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +94 -31
  267. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +270 -133
  268. package/src/msg_validators/attestation_validator/attestation_validator.ts +46 -15
  269. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +15 -7
  270. package/src/msg_validators/proposal_validator/block_proposal_validator.ts +15 -3
  271. package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +26 -3
  272. package/src/msg_validators/proposal_validator/proposal_validator.ts +94 -12
  273. package/src/msg_validators/tx_validator/README.md +13 -3
  274. package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +3 -12
  275. package/src/msg_validators/tx_validator/allowed_public_setup.ts +3 -3
  276. package/src/msg_validators/tx_validator/archive_cache.ts +1 -1
  277. package/src/msg_validators/tx_validator/cached_tx_validator.ts +31 -0
  278. package/src/msg_validators/tx_validator/data_validator.ts +2 -0
  279. package/src/msg_validators/tx_validator/factory.ts +37 -20
  280. package/src/msg_validators/tx_validator/gas_validator.ts +99 -54
  281. package/src/msg_validators/tx_validator/index.ts +2 -0
  282. package/src/msg_validators/tx_validator/phases_validator.ts +2 -2
  283. package/src/msg_validators/tx_validator/tx_proof_validator.ts +2 -0
  284. package/src/msg_validators/tx_validator/tx_validation_cache.ts +101 -0
  285. package/src/services/data_store.ts +5 -13
  286. package/src/services/discv5/discV5_service.ts +126 -14
  287. package/src/services/discv5/persisted_enr_store.ts +158 -0
  288. package/src/services/dummy_service.ts +25 -44
  289. package/src/services/encoding.ts +28 -20
  290. package/src/services/gossipsub/README.md +33 -11
  291. package/src/services/gossipsub/topic_score_params.ts +9 -18
  292. package/src/services/libp2p/instrumentation.ts +22 -0
  293. package/src/services/libp2p/libp2p_service.ts +433 -302
  294. package/src/services/peer-manager/peer_manager.ts +112 -16
  295. package/src/services/peer-manager/peer_scoring.ts +81 -3
  296. package/src/services/reqresp/README.md +2 -37
  297. package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +39 -52
  298. package/src/services/reqresp/batch-tx-requester/interface.ts +11 -4
  299. package/src/services/reqresp/batch-tx-requester/tx_validator.ts +12 -25
  300. package/src/services/reqresp/config.ts +19 -2
  301. package/src/services/reqresp/interface.ts +21 -47
  302. package/src/services/reqresp/metrics.ts +0 -1
  303. package/src/services/reqresp/protocols/block_txs/bitvector.ts +3 -2
  304. package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +34 -30
  305. package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +47 -20
  306. package/src/services/reqresp/protocols/index.ts +0 -1
  307. package/src/services/reqresp/protocols/tx.ts +1 -3
  308. package/src/services/reqresp/rate-limiter/rate_limits.ts +3 -13
  309. package/src/services/reqresp/reqresp.ts +69 -252
  310. package/src/services/service.ts +48 -37
  311. package/src/services/tx_collection/config.ts +3 -80
  312. package/src/services/tx_collection/file_store_tx_collection.ts +54 -103
  313. package/src/services/tx_collection/index.ts +1 -6
  314. package/src/services/tx_collection/instrumentation.ts +1 -7
  315. package/src/services/tx_collection/tx_collection.ts +330 -173
  316. package/src/services/tx_collection/tx_collection_sink.ts +2 -2
  317. package/src/services/tx_collection/tx_source.ts +2 -1
  318. package/src/services/tx_file_store/tx_file_store.ts +5 -17
  319. package/src/services/tx_provider.ts +5 -0
  320. package/src/test-helpers/make-test-p2p-clients.ts +3 -0
  321. package/src/test-helpers/mock-pubsub.ts +61 -60
  322. package/src/test-helpers/reqresp-nodes.ts +12 -22
  323. package/src/test-helpers/test_tx_provider.ts +5 -0
  324. package/src/test-helpers/testbench-utils.ts +35 -26
  325. package/src/testbench/p2p_client_testbench_worker.ts +68 -44
  326. package/src/testbench/worker_client_manager.ts +59 -19
  327. package/src/util.ts +32 -17
  328. package/src/versioning.ts +3 -33
  329. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts +0 -2
  330. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts.map +0 -1
  331. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +0 -315
  332. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts +0 -73
  333. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts.map +0 -1
  334. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.js +0 -8
  335. package/dest/msg_validators/clock_tolerance.d.ts +0 -21
  336. package/dest/msg_validators/clock_tolerance.d.ts.map +0 -1
  337. package/dest/msg_validators/clock_tolerance.js +0 -38
  338. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +0 -64
  339. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +0 -1
  340. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.js +0 -151
  341. package/dest/services/reqresp/protocols/block.d.ts +0 -9
  342. package/dest/services/reqresp/protocols/block.d.ts.map +0 -1
  343. package/dest/services/reqresp/protocols/block.js +0 -32
  344. package/dest/services/tx_collection/fast_tx_collection.d.ts +0 -51
  345. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +0 -1
  346. package/dest/services/tx_collection/fast_tx_collection.js +0 -311
  347. package/dest/services/tx_collection/proposal_tx_collector.d.ts +0 -48
  348. package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +0 -1
  349. package/dest/services/tx_collection/proposal_tx_collector.js +0 -50
  350. package/dest/services/tx_collection/slow_tx_collection.d.ts +0 -57
  351. package/dest/services/tx_collection/slow_tx_collection.d.ts.map +0 -1
  352. package/dest/services/tx_collection/slow_tx_collection.js +0 -211
  353. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +0 -355
  354. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.ts +0 -43
  355. package/src/msg_validators/clock_tolerance.ts +0 -52
  356. package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +0 -161
  357. package/src/services/reqresp/protocols/block.ts +0 -37
  358. package/src/services/tx_collection/fast_tx_collection.ts +0 -361
  359. package/src/services/tx_collection/proposal_tx_collector.ts +0 -108
  360. 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,9 +59,10 @@ 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
 
@@ -74,38 +83,34 @@ import {
74
83
  createFirstStageTxValidationsForGossipedTransactions,
75
84
  createSecondStageTxValidationsForGossipedTransactions,
76
85
  createTxValidatorForBlockProposalReceivedTxs,
77
- createTxValidatorForReqResponseReceivedTxs,
78
86
  } from '../../msg_validators/tx_validator/factory.js';
87
+ import { TxValidationCache } from '../../msg_validators/tx_validator/tx_validation_cache.js';
79
88
  import { GossipSubEvent } from '../../types/index.js';
80
89
  import { type PubSubLibp2p, convertToMultiaddr } from '../../util.js';
81
90
  import { getVersions } from '../../versioning.js';
82
91
  import { AztecDatastore } from '../data_store.js';
83
92
  import { DiscV5Service } from '../discv5/discV5_service.js';
84
- import { SnappyTransform, fastMsgIdFn, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
93
+ import { SnappyTransform, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
85
94
  import { APP_SPECIFIC_WEIGHT, gossipScoreThresholds } from '../gossipsub/scoring.js';
86
95
  import { createAllTopicScoreParams } from '../gossipsub/topic_score_params.js';
87
96
  import type { PeerManagerInterface } from '../peer-manager/interface.js';
88
97
  import { PeerManager } from '../peer-manager/peer_manager.js';
89
98
  import { PeerScoring } from '../peer-manager/peer_scoring.js';
90
99
  import type { BatchTxRequesterLibP2PService } from '../reqresp/batch-tx-requester/interface.js';
91
- import type { P2PReqRespConfig } from '../reqresp/config.js';
100
+ import { type P2PReqRespConfig, YAMUX_MAX_MESSAGE_SIZE_BYTES } from '../reqresp/config.js';
92
101
  import {
93
102
  AuthRequest,
94
103
  BlockTxsRequest,
95
104
  BlockTxsResponse,
96
- DEFAULT_SUB_PROTOCOL_VALIDATORS,
97
105
  type ReqRespInterface,
98
106
  type ReqRespResponse,
99
107
  ReqRespSubProtocol,
100
108
  type ReqRespSubProtocolHandler,
101
109
  type ReqRespSubProtocolHandlers,
102
- type ReqRespSubProtocolValidators,
103
110
  StatusMessage,
104
- type SubProtocolMap,
105
111
  ValidationError,
106
112
  pingHandler,
107
113
  reqGoodbyeHandler,
108
- reqRespBlockHandler,
109
114
  reqRespBlockTxsHandler,
110
115
  reqRespStatusHandler,
111
116
  reqRespTxHandler,
@@ -113,13 +118,33 @@ import {
113
118
  import { ReqResp } from '../reqresp/reqresp.js';
114
119
  import type {
115
120
  P2PBlockReceivedCallback,
121
+ P2PCheckpointAttestationCallback,
116
122
  P2PCheckpointReceivedCallback,
117
123
  P2PDuplicateAttestationCallback,
124
+ P2POversizedProposalCallback,
118
125
  P2PService,
119
126
  PeerDiscoveryService,
120
127
  } from '../service.js';
121
128
  import { P2PInstrumentation } from './instrumentation.js';
122
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
+
123
148
  interface ValidationResult {
124
149
  name: string;
125
150
  isValid: TxValidationResult;
@@ -148,8 +173,6 @@ export class LibP2PService extends WithTracer implements P2PService {
148
173
  private protocolVersion = '';
149
174
  private topicStrings: Record<TopicType, string> = {} as Record<TopicType, string>;
150
175
 
151
- private feesCache: { blockNumber: BlockNumber; gasFees: GasFees } | undefined;
152
-
153
176
  /** Callback invoked when a duplicate proposal is detected (triggers slashing). */
154
177
  private duplicateProposalCallback?: (info: {
155
178
  slot: SlotNumber;
@@ -157,9 +180,15 @@ export class LibP2PService extends WithTracer implements P2PService {
157
180
  type: 'checkpoint' | 'block';
158
181
  }) => void;
159
182
 
183
+ /** Callback invoked when an oversized block proposal is stored as slashing evidence (triggers slashing). */
184
+ private oversizedProposalCallback?: P2POversizedProposalCallback;
185
+
160
186
  /** Callback invoked when a duplicate attestation is detected (triggers slashing). */
161
187
  private duplicateAttestationCallback?: P2PDuplicateAttestationCallback;
162
188
 
189
+ /** Callback invoked when a valid checkpoint attestation is accepted into the pool. */
190
+ private checkpointAttestationCallback?: P2PCheckpointAttestationCallback;
191
+
163
192
  /**
164
193
  * Callback for when a block is received from a peer.
165
194
  * @param block - The block received from the peer.
@@ -182,6 +211,9 @@ export class LibP2PService extends WithTracer implements P2PService {
182
211
 
183
212
  private gossipSubEventHandler: (e: CustomEvent<GossipsubMessage>) => void;
184
213
 
214
+ private ipChangedHandler?: (ip: string) => void;
215
+ private discoveredP2pIp?: string;
216
+
185
217
  private instrumentation: P2PInstrumentation;
186
218
 
187
219
  private telemetry: TelemetryClient;
@@ -199,8 +231,10 @@ export class LibP2PService extends WithTracer implements P2PService {
199
231
  private epochCache: EpochCacheInterface,
200
232
  private proofVerifier: ClientProtocolCircuitVerifier,
201
233
  private worldStateSynchronizer: WorldStateSynchronizer,
234
+ private blockMinFeesProvider: BlockMinFeesProvider,
202
235
  telemetry: TelemetryClient,
203
236
  logger: Logger = createLogger('p2p:libp2p_service'),
237
+ private txValidationCache?: TxValidationCache,
204
238
  ) {
205
239
  super(telemetry, 'LibP2PService');
206
240
  this.telemetry = telemetry;
@@ -230,15 +264,39 @@ export class LibP2PService extends WithTracer implements P2PService {
230
264
  this.protocolVersion,
231
265
  );
232
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());
233
270
  const proposalValidatorOpts = {
234
271
  txsPermitted: !config.disableTransactions,
235
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,
236
290
  };
237
- this.blockProposalValidator = new BlockProposalValidator(epochCache, proposalValidatorOpts);
238
- this.checkpointProposalValidator = new CheckpointProposalValidator(epochCache, proposalValidatorOpts);
239
291
  this.checkpointAttestationValidator = config.fishermanMode
240
- ? new FishermanAttestationValidator(epochCache, mempools.attestationPool, telemetry)
241
- : new CheckpointAttestationValidator(epochCache);
292
+ ? new FishermanAttestationValidator(
293
+ epochCache,
294
+ consensusTimetable,
295
+ mempools.attestationPool,
296
+ telemetry,
297
+ attestationValidatorOpts,
298
+ )
299
+ : new CheckpointAttestationValidator(epochCache, consensusTimetable, attestationValidatorOpts);
242
300
 
243
301
  this.gossipSubEventHandler = this.handleGossipSubEvent.bind(this);
244
302
 
@@ -251,20 +309,21 @@ export class LibP2PService extends WithTracer implements P2PService {
251
309
  };
252
310
 
253
311
  this.allNodesCheckpointReceivedCallback = (
254
- _checkpoint: CheckpointProposalCore,
312
+ _checkpoint: ValidatedCheckpointProposalCore,
255
313
  ): Promise<CheckpointAttestation[] | undefined> => {
256
314
  throw new CheckpointProposalReceivedCallbackNotRegisteredError();
257
315
  };
258
316
 
259
317
  this.validatorCheckpointReceivedCallback = (
260
- _checkpoint: CheckpointProposalCore,
318
+ _checkpoint: ValidatedCheckpointProposalCore,
261
319
  ): Promise<CheckpointAttestation[] | undefined> => {
262
320
  return Promise.resolve(undefined);
263
321
  };
264
322
  }
265
323
 
266
- public updateConfig(config: Partial<P2PReqRespConfig>) {
324
+ public updateConfig(config: Partial<P2PReqRespConfig & Pick<P2PConfig, 'skipIncomingProposals'>>) {
267
325
  this.reqresp.updateConfig(config);
326
+ this.config = merge(this.config, config);
268
327
  }
269
328
 
270
329
  /**
@@ -283,9 +342,11 @@ export class LibP2PService extends WithTracer implements P2PService {
283
342
  proofVerifier: ClientProtocolCircuitVerifier;
284
343
  worldStateSynchronizer: WorldStateSynchronizer;
285
344
  peerStore: AztecAsyncKVStore;
345
+ blockMinFeesProvider: BlockMinFeesProvider;
286
346
  telemetry: TelemetryClient;
287
347
  logger: Logger;
288
348
  packageVersion: string;
349
+ txValidationCache?: TxValidationCache;
289
350
  },
290
351
  ) {
291
352
  const {
@@ -295,9 +356,11 @@ export class LibP2PService extends WithTracer implements P2PService {
295
356
  mempools,
296
357
  proofVerifier,
297
358
  peerStore,
359
+ blockMinFeesProvider,
298
360
  telemetry,
299
361
  logger,
300
362
  packageVersion,
363
+ txValidationCache,
301
364
  } = deps;
302
365
  const { p2pPort, maxPeerCount, listenAddress } = config;
303
366
  const bindAddrTcp = convertToMultiaddr(listenAddress, p2pPort, 'tcp');
@@ -312,6 +375,7 @@ export class LibP2PService extends WithTracer implements P2PService {
312
375
  packageVersion,
313
376
  telemetry,
314
377
  createLogger(`${logger.module}:discv5_service`, logger.getBindings()),
378
+ peerStore,
315
379
  );
316
380
 
317
381
  // Seed libp2p's bootstrap discovery with private and trusted peers
@@ -326,34 +390,49 @@ export class LibP2PService extends WithTracer implements P2PService {
326
390
  const protocolVersion = compressComponentVersions(versions);
327
391
 
328
392
  const preferredPeersEnrs: ENR[] = config.preferredPeers.map(enr => ENR.decodeTxt(enr));
329
- const directPeers = (
393
+ const directPeers = compactArray(
330
394
  await Promise.all(
331
395
  preferredPeersEnrs.map(async enr => {
332
- const peerId = await enr.peerId();
333
- const address = enr.getLocationMultiaddr('tcp');
334
- if (address === undefined) {
335
- 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;
336
410
  }
337
- return {
338
- id: peerId,
339
- addrs: [address],
340
- };
341
411
  }),
342
- )
343
- ).filter(peer => peer !== undefined);
412
+ ),
413
+ );
344
414
 
345
415
  const announceTcpMultiaddr = config.p2pIp ? [convertToMultiaddr(config.p2pIp, p2pPort, 'tcp')] : [];
346
416
 
347
- // 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.
348
420
  const l1Constants = epochCache.getL1Constants();
349
421
  const topicScoreParams = createAllTopicScoreParams(protocolVersion, {
350
422
  slotDurationMs: l1Constants.slotDuration * 1000,
351
423
  heartbeatIntervalMs: config.gossipsubInterval,
352
424
  targetCommitteeSize: l1Constants.targetCommitteeSize,
353
- blockDurationMs: config.blockDurationMs,
425
+ maxBlocksPerCheckpoint: config.maxBlocksPerCheckpoint ?? DEFAULT_MAX_BLOCKS_PER_CHECKPOINT,
354
426
  expectedBlockProposalsPerSlot: config.expectedBlockProposalsPerSlot,
355
427
  });
356
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
+
357
436
  const node = await createLibp2p({
358
437
  start: false,
359
438
  peerId,
@@ -385,7 +464,8 @@ export class LibP2PService extends WithTracer implements P2PService {
385
464
  ],
386
465
  datastore,
387
466
  peerDiscovery,
388
- 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()],
389
469
  connectionEncryption: [noise()],
390
470
  connectionManager: {
391
471
  minConnections: 0, // Disable libp2p peer dialing, we do it manually
@@ -399,7 +479,7 @@ export class LibP2PService extends WithTracer implements P2PService {
399
479
  },
400
480
  connectionGater: {
401
481
  denyInboundConnection: (maConn: MultiaddrConnection) => {
402
- const allowed = peerManager.isNodeAllowedToConnect(maConn.remoteAddr.nodeAddress().address);
482
+ const allowed = peerManager.isAddressAllowedToConnect(maConn.remoteAddr.nodeAddress().address);
403
483
  if (allowed) {
404
484
  return false;
405
485
  }
@@ -410,7 +490,7 @@ export class LibP2PService extends WithTracer implements P2PService {
410
490
  denyInboundEncryptedConnection: (peerId: PeerId, _maConn: MultiaddrConnection) => {
411
491
  //NOTE: it is not necessary to check address here because this was already done by
412
492
  // denyInboundConnection
413
- const allowed = peerManager.isNodeAllowedToConnect(peerId);
493
+ const allowed = peerManager.isPeerAllowedToConnect(peerId);
414
494
  if (allowed) {
415
495
  return false;
416
496
  }
@@ -438,9 +518,13 @@ export class LibP2PService extends WithTracer implements P2PService {
438
518
  mcacheLength: config.gossipsubMcacheLength,
439
519
  mcacheGossip: config.gossipsubMcacheGossip,
440
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.
441
526
  msgIdFn: getMsgIdFn,
442
527
  msgIdToStrFn: msgIdToStrFn,
443
- fastMsgIdFn: fastMsgIdFn,
444
528
  dataTransform: new SnappyTransform(),
445
529
  metricsRegister: otelMetricsAdapter,
446
530
  metricsTopicStrToLabel: metricsTopicStrToLabels(protocolVersion),
@@ -452,8 +536,9 @@ export class LibP2PService extends WithTracer implements P2PService {
452
536
  topics: topicScoreParams,
453
537
  }),
454
538
  }) as (components: GossipSubComponents) => GossipSub,
455
- components: (components: { connectionManager: ConnectionManager }) => ({
539
+ components: (components: { connectionManager: ConnectionManager; addressManager: AddressManager }) => ({
456
540
  connectionManager: components.connectionManager,
541
+ addressManager: components.addressManager,
457
542
  }),
458
543
  },
459
544
  logger: createLibp2pComponentLogger(logger.module, logger.getBindings()),
@@ -475,6 +560,9 @@ export class LibP2PService extends WithTracer implements P2PService {
475
560
  epochCache,
476
561
  );
477
562
 
563
+ // Gate req/resp data protocols for unauthenticated peers when p2pAllowOnlyValidators is enabled
564
+ reqresp.setShouldRejectPeer(peerId => peerManager.shouldDisableP2PGossip(peerId));
565
+
478
566
  // Configure application-specific scoring for gossipsub.
479
567
  // The weight scales app score to align with gossipsub thresholds:
480
568
  // - Disconnect (-50) × 10 = -500 = gossipThreshold (stops receiving gossip)
@@ -495,8 +583,10 @@ export class LibP2PService extends WithTracer implements P2PService {
495
583
  epochCache,
496
584
  proofVerifier,
497
585
  worldStateSynchronizer,
586
+ blockMinFeesProvider,
498
587
  telemetry,
499
588
  logger,
589
+ txValidationCache,
500
590
  );
501
591
  }
502
592
 
@@ -510,24 +600,21 @@ export class LibP2PService extends WithTracer implements P2PService {
510
600
  throw new Error('P2P service already started');
511
601
  }
512
602
 
513
- // Get listen & announce addresses for logging
514
603
  const { p2pIp, p2pPort } = this.config;
515
- if (!p2pIp) {
516
- 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.');
517
606
  }
518
- const announceTcpMultiaddr = convertToMultiaddr(p2pIp, p2pPort, 'tcp');
607
+ const announceTcpMultiaddr = p2pIp ? convertToMultiaddr(p2pIp, p2pPort, 'tcp') : undefined;
519
608
 
520
609
  // Create request response protocol handlers
521
610
  const txHandler = reqRespTxHandler(this.mempools);
522
611
  const goodbyeHandler = reqGoodbyeHandler(this.peerManager);
523
- const blockHandler = reqRespBlockHandler(this.archiver);
524
612
  const statusHandler = reqRespStatusHandler(this.protocolVersion, this.worldStateSynchronizer, this.logger);
525
613
 
526
614
  const requestResponseHandlers: Partial<ReqRespSubProtocolHandlers> = {
527
615
  [ReqRespSubProtocol.PING]: pingHandler,
528
616
  [ReqRespSubProtocol.STATUS]: statusHandler.bind(this),
529
617
  [ReqRespSubProtocol.GOODBYE]: goodbyeHandler.bind(this),
530
- [ReqRespSubProtocol.BLOCK]: blockHandler.bind(this),
531
618
  };
532
619
 
533
620
  if (!this.config.disableTransactions) {
@@ -543,17 +630,9 @@ export class LibP2PService extends WithTracer implements P2PService {
543
630
  requestResponseHandlers[ReqRespSubProtocol.TX] = txHandler.bind(this);
544
631
  }
545
632
 
546
- // Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
547
- const reqrespSubProtocolValidators = {
548
- ...DEFAULT_SUB_PROTOCOL_VALIDATORS,
549
- [ReqRespSubProtocol.TX]: this.validateRequestedTxs.bind(this),
550
- [ReqRespSubProtocol.BLOCK_TXS]: this.validateRequestedBlockTxs.bind(this),
551
- [ReqRespSubProtocol.BLOCK]: this.validateRequestedBlock.bind(this),
552
- };
553
-
554
633
  await this.peerManager.initializePeers();
555
634
 
556
- await this.reqresp.start(requestResponseHandlers, reqrespSubProtocolValidators);
635
+ await this.reqresp.start(requestResponseHandlers);
557
636
 
558
637
  await this.node.start();
559
638
 
@@ -569,6 +648,38 @@ export class LibP2PService extends WithTracer implements P2PService {
569
648
  if (!this.config.p2pDiscoveryDisabled) {
570
649
  await this.peerDiscoveryService.start();
571
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
+
572
683
  this.discoveryRunningPromise = new RunningPromise(
573
684
  async () => {
574
685
  await this.peerManager.heartbeat();
@@ -594,6 +705,11 @@ export class LibP2PService extends WithTracer implements P2PService {
594
705
  // Remove gossip sub listener
595
706
  this.node.services.pubsub.removeEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
596
707
 
708
+ if (this.ipChangedHandler) {
709
+ this.peerDiscoveryService.removeListener('ip:changed', this.ipChangedHandler);
710
+ this.ipChangedHandler = undefined;
711
+ }
712
+
597
713
  // Stop peer manager
598
714
  this.logger.debug('Stopping peer manager...');
599
715
  await this.peerManager.stop();
@@ -608,12 +724,8 @@ export class LibP2PService extends WithTracer implements P2PService {
608
724
  this.logger.info('LibP2P service stopped');
609
725
  }
610
726
 
611
- addReqRespSubProtocol(
612
- subProtocol: ReqRespSubProtocol,
613
- handler: ReqRespSubProtocolHandler,
614
- validator?: ReqRespSubProtocolValidators[ReqRespSubProtocol],
615
- ): Promise<void> {
616
- return this.reqresp.addSubProtocol(subProtocol, handler, validator);
727
+ addReqRespSubProtocol(subProtocol: ReqRespSubProtocol, handler: ReqRespSubProtocolHandler): Promise<void> {
728
+ return this.reqresp.addSubProtocol(subProtocol, handler);
617
729
  }
618
730
 
619
731
  public registerThisValidatorAddresses(address: EthAddress[]): void {
@@ -624,6 +736,11 @@ export class LibP2PService extends WithTracer implements P2PService {
624
736
  return this.peerManager.getPeers(includePending);
625
737
  }
626
738
 
739
+ public getP2PConnectivity(): P2PConnectivity {
740
+ const connectedPeers = this.peerManager.getPeers().filter(peer => peer.status === 'connected').length;
741
+ return { enabled: true, connectedPeers };
742
+ }
743
+
627
744
  public getGossipMeshPeerCount(topicType: TopicType): number {
628
745
  return this.node.services.pubsub.getMeshPeers(this.topicStrings[topicType]).length;
629
746
  }
@@ -641,20 +758,6 @@ export class LibP2PService extends WithTracer implements P2PService {
641
758
  setImmediate(() => void safeJob());
642
759
  }
643
760
 
644
- /**
645
- * Send a batch of requests to peers, and return the responses
646
- * @param protocol - The request response protocol to use
647
- * @param requests - The requests to send to the peers
648
- * @returns The responses to the requests
649
- */
650
- sendBatchRequest<SubProtocol extends ReqRespSubProtocol>(
651
- protocol: SubProtocol,
652
- requests: InstanceType<SubProtocolMap[SubProtocol]['request']>[],
653
- pinnedPeerId: PeerId | undefined,
654
- ): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']>[]> {
655
- return this.reqresp.sendBatchRequest(protocol, requests, pinnedPeerId);
656
- }
657
-
658
761
  public sendRequestToPeer(
659
762
  peerId: PeerId,
660
763
  subProtocol: ReqRespSubProtocol,
@@ -684,10 +787,6 @@ export class LibP2PService extends WithTracer implements P2PService {
684
787
  this.allNodesCheckpointReceivedCallback = callback;
685
788
  }
686
789
 
687
- public async notifyOwnCheckpointProposal(checkpoint: CheckpointProposalCore): Promise<void> {
688
- await this.allNodesCheckpointReceivedCallback(checkpoint, this.node.peerId);
689
- }
690
-
691
790
  /**
692
791
  * Registers a callback to be invoked when a duplicate proposal is detected.
693
792
  * This callback is triggered on the first duplicate (when count goes from 1 to 2).
@@ -698,6 +797,13 @@ export class LibP2PService extends WithTracer implements P2PService {
698
797
  this.duplicateProposalCallback = callback;
699
798
  }
700
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
+
701
807
  /**
702
808
  * Registers a callback to be invoked when a duplicate attestation is detected.
703
809
  * A validator signing attestations for different proposals at the same slot.
@@ -707,6 +813,10 @@ export class LibP2PService extends WithTracer implements P2PService {
707
813
  this.duplicateAttestationCallback = callback;
708
814
  }
709
815
 
816
+ public registerCheckpointAttestationCallback(callback: P2PCheckpointAttestationCallback): void {
817
+ this.checkpointAttestationCallback = callback;
818
+ }
819
+
710
820
  /**
711
821
  * Subscribes to a topic.
712
822
  * @param topic - The topic to subscribe to.
@@ -835,6 +945,15 @@ export class LibP2PService extends WithTracer implements P2PService {
835
945
 
836
946
  // Process the message, optionally within a linked span for trace propagation
837
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
+ }
838
957
  if (msg.topic === this.topicStrings[TopicType.tx]) {
839
958
  await this.handleGossipedTx(p2pMessage.payload, msgId, source);
840
959
  } else if (msg.topic === this.topicStrings[TopicType.checkpoint_attestation]) {
@@ -899,6 +1018,7 @@ export class LibP2PService extends WithTracer implements P2PService {
899
1018
  msgId: string,
900
1019
  source: PeerId,
901
1020
  topicType: TopicType,
1021
+ stageTimings?: Record<string, number>,
902
1022
  ): Promise<ReceivedMessageValidationResult<T, M>> {
903
1023
  // Default to reject result with a penalty if validation function throws an error
904
1024
  let resultAndObj: ReceivedMessageValidationResult<T, M> = {
@@ -912,8 +1032,25 @@ export class LibP2PService extends WithTracer implements P2PService {
912
1032
  this.logger.error(`Error validating gossipsub message`, err, { msgId, source: source.toString(), topicType });
913
1033
  }
914
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
+
915
1046
  if (resultAndObj.result === TopicValidatorResult.Accept) {
916
- 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
+ });
917
1054
  this.instrumentation.recordMessageValidation(topicType, timer);
918
1055
  } else if (resultAndObj.result === TopicValidatorResult.Reject) {
919
1056
  this.logger.warn(`Message ${topicType} rejected by validator with severity ${resultAndObj.severity}`, {
@@ -945,18 +1082,38 @@ export class LibP2PService extends WithTracer implements P2PService {
945
1082
  }
946
1083
 
947
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
+
948
1098
  const validationFunc: () => Promise<ReceivedMessageValidationResult<Tx>> = async () => {
949
- const tx = this.tryDeserialize(() => Tx.fromBuffer(payloadData), msgId, source);
1099
+ const tx = await timed('deserialize', () => this.tryDeserialize(() => Tx.fromBuffer(payloadData), msgId, source));
950
1100
  if (!tx) {
951
1101
  return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.LowToleranceError };
952
1102
  }
953
1103
 
954
- const currentBlockNumber = await this.archiver.getBlockNumber();
955
- 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
+ });
956
1114
 
957
1115
  // Stage 1: fast validators (metadata, data, timestamps, double-spend, gas, phases, block header)
958
- const firstStageValidators = await this.createFirstStageMessageValidators(currentBlockNumber, nextSlotTimestamp);
959
- const firstStageOutcome = await this.runValidations(tx, firstStageValidators);
1116
+ const firstStageOutcome = await timed('fast_validation', () => this.runValidations(tx, firstStageValidators));
960
1117
  if (!firstStageOutcome.allPassed) {
961
1118
  const { name } = firstStageOutcome.failure;
962
1119
  let { severity } = firstStageOutcome.failure;
@@ -978,7 +1135,7 @@ export class LibP2PService extends WithTracer implements P2PService {
978
1135
  }
979
1136
 
980
1137
  // Pool pre-check: see if the pool would accept this tx before doing expensive proof verification
981
- const canAdd = await this.mempools.txPool.canAddPendingTx(tx);
1138
+ const canAdd = await timed('pool_precheck', () => this.mempools.txPool.canAddPendingTx(tx));
982
1139
  if (canAdd === 'ignored') {
983
1140
  this.logger.verbose(`Ignoring gossiped tx ${tx.getTxHash().toString()}: pool pre-check returned ignored`, {
984
1141
  source: source.toString(),
@@ -988,7 +1145,7 @@ export class LibP2PService extends WithTracer implements P2PService {
988
1145
 
989
1146
  // Stage 2: expensive proof verification
990
1147
  const secondStageValidators = this.createSecondStageMessageValidators();
991
- const secondStageOutcome = await this.runValidations(tx, secondStageValidators);
1148
+ const secondStageOutcome = await timed('proof_verify', () => this.runValidations(tx, secondStageValidators));
992
1149
  if (!secondStageOutcome.allPassed) {
993
1150
  const { severity, name } = secondStageOutcome.failure;
994
1151
  this.logger.verbose(`Rejecting gossiped tx ${tx.getTxHash().toString()}: stage 2 validation failed`, {
@@ -1001,7 +1158,7 @@ export class LibP2PService extends WithTracer implements P2PService {
1001
1158
 
1002
1159
  // Pool add: persist the tx
1003
1160
  const txHash = tx.getTxHash();
1004
- const addResult = await this.mempools.txPool.addPendingTxs([tx], { source: 'gossip' });
1161
+ const addResult = await timed('pool_add', () => this.mempools.txPool.addPendingTxs([tx], { source: 'gossip' }));
1005
1162
 
1006
1163
  const wasAccepted = addResult.accepted.some(h => h.equals(txHash));
1007
1164
  const wasIgnored = addResult.ignored.some(h => h.equals(txHash));
@@ -1025,7 +1182,13 @@ export class LibP2PService extends WithTracer implements P2PService {
1025
1182
  }
1026
1183
  };
1027
1184
 
1028
- 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
+ );
1029
1192
  if (result !== TopicValidatorResult.Accept || !tx) {
1030
1193
  return;
1031
1194
  }
@@ -1147,6 +1310,7 @@ export class LibP2PService extends WithTracer implements P2PService {
1147
1310
  }
1148
1311
 
1149
1312
  // Attestation was added successfully - accept it so other nodes can also detect the equivocation
1313
+ this.checkpointAttestationCallback?.(attestation);
1150
1314
  return { result: TopicValidatorResult.Accept, obj: attestation };
1151
1315
  }
1152
1316
 
@@ -1154,20 +1318,21 @@ export class LibP2PService extends WithTracer implements P2PService {
1154
1318
  const {
1155
1319
  result,
1156
1320
  obj: block,
1157
- metadata: { isEquivocated } = {},
1158
- } = await this.validateReceivedMessage<BlockProposal, { isEquivocated: boolean }>(
1321
+ metadata: { isEquivocated, isOversized } = {},
1322
+ } = await this.validateReceivedMessage<BlockProposal, { isEquivocated: boolean; isOversized: boolean }>(
1159
1323
  () => this.validateAndStoreBlockProposal(source, BlockProposal.fromBuffer(payloadData)),
1160
1324
  msgId,
1161
1325
  source,
1162
1326
  TopicType.block_proposal,
1163
1327
  );
1164
1328
 
1165
- // If not accepted or equivocated, return
1166
- 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) {
1167
1332
  return;
1168
1333
  }
1169
1334
 
1170
- await this.processValidBlockProposal(block, source);
1335
+ await this.processValidBlockProposal(ValidatedBlockProposal(block), source);
1171
1336
  }
1172
1337
 
1173
1338
  /** Validates a block proposal. Triggers a penalization to the peer that sent it if invalid. Adds to the mempool if valid. */
@@ -1178,7 +1343,7 @@ export class LibP2PService extends WithTracer implements P2PService {
1178
1343
  protected async validateAndStoreBlockProposal(
1179
1344
  peerId: PeerId,
1180
1345
  block: BlockProposal,
1181
- ): Promise<ReceivedMessageValidationResult<BlockProposal, { isEquivocated: boolean }>> {
1346
+ ): Promise<ReceivedMessageValidationResult<BlockProposal, { isEquivocated: boolean; isOversized: boolean }>> {
1182
1347
  const validationResult = await this.blockProposalValidator.validate(block);
1183
1348
 
1184
1349
  if (validationResult.result === 'reject') {
@@ -1193,6 +1358,12 @@ export class LibP2PService extends WithTracer implements P2PService {
1193
1358
  // Try to add the proposal: this handles existence check, cap check, and adding in one call
1194
1359
  const { added, alreadyExists, count } = await this.mempools.attestationPool.tryAddBlockProposal(block);
1195
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;
1196
1367
 
1197
1368
  // Duplicate proposal received, no need to re-broadcast
1198
1369
  if (alreadyExists) {
@@ -1202,7 +1373,7 @@ export class LibP2PService extends WithTracer implements P2PService {
1202
1373
  proposer: block.getSender()?.toString(),
1203
1374
  source: peerId.toString(),
1204
1375
  });
1205
- return { result: TopicValidatorResult.Ignore, obj: block, metadata: { isEquivocated } };
1376
+ return { result: TopicValidatorResult.Ignore, obj: block, metadata: { isEquivocated, isOversized } };
1206
1377
  }
1207
1378
 
1208
1379
  // Too many blocks received for this slot and index, penalize peer and do not re-broadcast
@@ -1216,11 +1387,27 @@ export class LibP2PService extends WithTracer implements P2PService {
1216
1387
  });
1217
1388
  return {
1218
1389
  result: TopicValidatorResult.Reject,
1219
- metadata: { isEquivocated },
1390
+ metadata: { isEquivocated, isOversized },
1220
1391
  severity: PeerErrorSeverity.HighToleranceError,
1221
1392
  };
1222
1393
  }
1223
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
+
1224
1411
  // If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
1225
1412
  // and do re-broadcast it so other nodes in the network know to slash the proposer
1226
1413
  if (isEquivocated) {
@@ -1234,11 +1421,11 @@ export class LibP2PService extends WithTracer implements P2PService {
1234
1421
  if (proposer && count === 2) {
1235
1422
  this.duplicateProposalCallback?.({ slot: block.slotNumber, proposer, type: 'block' });
1236
1423
  }
1237
- return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated } };
1424
+ return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated, isOversized } };
1238
1425
  }
1239
1426
 
1240
1427
  // Otherwise, we're good to go!
1241
- return { result: TopicValidatorResult.Accept, obj: block };
1428
+ return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated: false, isOversized } };
1242
1429
  }
1243
1430
 
1244
1431
  // REFACTOR(palla): This method should be moved to the p2p_client or to a separate component,
@@ -1248,7 +1435,7 @@ export class LibP2PService extends WithTracer implements P2PService {
1248
1435
  [Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
1249
1436
  [Attributes.P2P_ID]: await block.p2pMessageLoggingIdentifier().then(i => i.toString()),
1250
1437
  }))
1251
- protected async processValidBlockProposal(block: BlockProposal, sender: PeerId) {
1438
+ protected async processValidBlockProposal(block: ValidatedBlockProposal, sender: PeerId) {
1252
1439
  const slot = block.slotNumber;
1253
1440
  this.logger.verbose(`Received block proposal for slot ${slot} from external peer ${sender.toString()}.`, {
1254
1441
  p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier(),
@@ -1259,11 +1446,29 @@ export class LibP2PService extends WithTracer implements P2PService {
1259
1446
  // Mark the txs in this proposal as protected
1260
1447
  await this.mempools.txPool.protectTxs(block.txHashes, block.blockHeader);
1261
1448
 
1262
- // Call the block received callback to validate the proposal.
1263
- // Note: Validators do NOT attest to individual blocks, only to checkpoint proposals.
1264
- const isValid = await this.blockReceivedCallback(block, sender);
1265
- if (!isValid) {
1266
- 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;
1267
1472
  }
1268
1473
  }
1269
1474
 
@@ -1275,25 +1480,45 @@ export class LibP2PService extends WithTracer implements P2PService {
1275
1480
  const {
1276
1481
  result,
1277
1482
  obj: checkpoint,
1278
- metadata: { isEquivocated, processBlock } = {},
1279
- } = 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
+ >(
1280
1488
  () => this.validateAndStoreCheckpointProposal(source, CheckpointProposal.fromBuffer(payloadData)),
1281
1489
  msgId,
1282
1490
  source,
1283
1491
  TopicType.checkpoint_proposal,
1284
1492
  );
1285
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
+
1286
1501
  // If the checkpoint contained a valid last block, we process it even if the checkpoint itself is to be rejected
1287
1502
  // TODO(palla/mbps): Is this ok? Should we be considering a block from a checkpoint that was equivocated?
1288
- if (processBlock && checkpoint?.getBlockProposal()) {
1289
- await this.processValidBlockProposal(checkpoint.getBlockProposal()!, source);
1290
- }
1291
-
1292
- 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();
1293
1516
  return;
1294
1517
  }
1295
1518
 
1296
- 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();
1297
1522
  }
1298
1523
 
1299
1524
  /**
@@ -1306,7 +1531,12 @@ export class LibP2PService extends WithTracer implements P2PService {
1306
1531
  protected async validateAndStoreCheckpointProposal(
1307
1532
  peerId: PeerId,
1308
1533
  checkpoint: CheckpointProposal,
1309
- ): Promise<ReceivedMessageValidationResult<CheckpointProposal, { isEquivocated: boolean; processBlock: boolean }>> {
1534
+ ): Promise<
1535
+ ReceivedMessageValidationResult<
1536
+ CheckpointProposal,
1537
+ { isEquivocated: boolean; processBlock: boolean; isOversized: boolean }
1538
+ >
1539
+ > {
1310
1540
  const validationResult = await this.checkpointProposalValidator.validate(checkpoint);
1311
1541
 
1312
1542
  if (validationResult.result === 'reject') {
@@ -1321,13 +1551,16 @@ export class LibP2PService extends WithTracer implements P2PService {
1321
1551
  // Extract and try to add the block proposal first if present
1322
1552
  const blockProposal = checkpoint.getBlockProposal();
1323
1553
  let processBlock = false;
1554
+ let isOversized = false;
1324
1555
  if (blockProposal) {
1325
1556
  this.logger.debug(`Validating block proposal from propagated checkpoint`, {
1326
1557
  [Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
1327
1558
  [Attributes.P2P_ID]: peerId.toString(),
1328
1559
  });
1329
1560
  const blockProposalResult = await this.validateAndStoreBlockProposal(peerId, blockProposal);
1330
- const { obj, metadata: { isEquivocated } = {} } = blockProposalResult;
1561
+ const { obj, metadata: { isEquivocated, isOversized: blockIsOversized } = {} } = blockProposalResult;
1562
+ isOversized = blockIsOversized ?? false;
1563
+
1331
1564
  if (blockProposalResult.result === TopicValidatorResult.Reject || !obj || isEquivocated) {
1332
1565
  this.logger.debug(`Rejecting checkpoint due to invalid last block proposal`, {
1333
1566
  [Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
@@ -1340,7 +1573,8 @@ export class LibP2PService extends WithTracer implements P2PService {
1340
1573
  severity:
1341
1574
  'severity' in blockProposalResult ? blockProposalResult.severity : PeerErrorSeverity.MidToleranceError,
1342
1575
  };
1343
- } 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.
1344
1578
  processBlock = true;
1345
1579
  }
1346
1580
  }
@@ -1360,11 +1594,11 @@ export class LibP2PService extends WithTracer implements P2PService {
1360
1594
  return {
1361
1595
  result: TopicValidatorResult.Ignore,
1362
1596
  obj: checkpoint,
1363
- metadata: { isEquivocated, processBlock },
1597
+ metadata: { isEquivocated, processBlock, isOversized },
1364
1598
  };
1365
1599
  }
1366
1600
 
1367
- // 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.
1368
1602
  // Note: We still return the checkpoint obj so the lastBlock can be processed if valid
1369
1603
  if (!added) {
1370
1604
  this.logger.warn(`Penalizing peer for checkpoint proposal exceeding per-slot cap`, {
@@ -1375,7 +1609,7 @@ export class LibP2PService extends WithTracer implements P2PService {
1375
1609
  return {
1376
1610
  result: TopicValidatorResult.Reject,
1377
1611
  obj: checkpoint,
1378
- metadata: { isEquivocated, processBlock },
1612
+ metadata: { isEquivocated, processBlock, isOversized },
1379
1613
  severity: PeerErrorSeverity.HighToleranceError,
1380
1614
  };
1381
1615
  }
@@ -1396,12 +1630,16 @@ export class LibP2PService extends WithTracer implements P2PService {
1396
1630
  return {
1397
1631
  result: TopicValidatorResult.Accept,
1398
1632
  obj: checkpoint,
1399
- metadata: { isEquivocated, processBlock },
1633
+ metadata: { isEquivocated, processBlock, isOversized },
1400
1634
  };
1401
1635
  }
1402
1636
 
1403
1637
  // Otherwise, we're good to go!
1404
- return { result: TopicValidatorResult.Accept, obj: checkpoint, metadata: { processBlock, isEquivocated } };
1638
+ return {
1639
+ result: TopicValidatorResult.Accept,
1640
+ obj: checkpoint,
1641
+ metadata: { processBlock, isEquivocated, isOversized },
1642
+ };
1405
1643
  }
1406
1644
 
1407
1645
  /**
@@ -1413,7 +1651,7 @@ export class LibP2PService extends WithTracer implements P2PService {
1413
1651
  [Attributes.BLOCK_ARCHIVE]: checkpoint.archive.toString(),
1414
1652
  [Attributes.P2P_ID]: await checkpoint.p2pMessageLoggingIdentifier().then(i => i.toString()),
1415
1653
  }))
1416
- protected async processValidCheckpointProposal(checkpoint: CheckpointProposalCore, sender: PeerId) {
1654
+ protected async processValidCheckpointProposal(checkpoint: ValidatedCheckpointProposalCore, sender: PeerId) {
1417
1655
  const slot = checkpoint.slotNumber;
1418
1656
  this.logger.verbose(`Received checkpoint proposal for slot ${slot} from external peer ${sender.toString()}.`, {
1419
1657
  p2pMessageIdentifier: await checkpoint.p2pMessageLoggingIdentifier(),
@@ -1449,82 +1687,87 @@ export class LibP2PService extends WithTracer implements P2PService {
1449
1687
  }
1450
1688
 
1451
1689
  /**
1452
- * 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.
1453
1692
  * @param request - The block transactions request.
1454
1693
  * @param response - The block transactions response.
1455
1694
  * @param peerId - The ID of the peer that made the request.
1456
- * @returns True if the requested block transactions are valid, false otherwise.
1695
+ * @returns True if the request-response is consistent, false otherwise.
1457
1696
  */
1458
- @trackSpan('Libp2pService.validateRequestedBlockTxs', request => ({
1697
+ @trackSpan('Libp2pService.validateRequestedBlockTxsConsistency', request => ({
1459
1698
  [Attributes.BLOCK_ARCHIVE]: request.archiveRoot.toString(),
1460
1699
  }))
1461
- protected async validateRequestedBlockTxs(
1700
+ protected async validateRequestedBlockTxsConsistency(
1462
1701
  request: BlockTxsRequest,
1463
1702
  response: BlockTxsResponse,
1464
1703
  peerId: PeerId,
1465
1704
  ): Promise<boolean> {
1466
- const requestedTxValidator = this.createRequestedTxValidator();
1467
-
1468
1705
  try {
1469
- 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) {
1470
1710
  this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1471
- throw new ValidationError(
1472
- `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`,
1473
1727
  );
1728
+ // NOTE: We mark the response as invalid, given the limitations of a true/false return value.
1729
+ return false;
1474
1730
  }
1475
1731
 
1476
- if (response.txIndices.getLength() !== request.txIndices.getLength()) {
1477
- 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);
1478
1741
  throw new ValidationError(
1479
- `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)',
1480
1743
  );
1481
1744
  }
1482
1745
 
1483
- // Check no duplicates and not exceeding returnable count
1484
- const requestedIndices = new Set(request.txIndices.getTrueIndices());
1485
- const availableIndices = new Set(response.txIndices.getTrueIndices());
1486
- const maxReturnable = [...requestedIndices].filter(i => availableIndices.has(i)).length;
1487
-
1488
- const returnedHashes = await Promise.all(response.txs.map(tx => tx.getTxHash().toString()));
1489
- const uniqueReturned = new Set(returnedHashes.map(h => h.toString()));
1490
- if (uniqueReturned.size !== returnedHashes.length) {
1491
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1492
- 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;
1493
1750
  }
1494
- 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) {
1495
1754
  this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1496
1755
  throw new ValidationError(
1497
- `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()}`,
1498
1757
  );
1499
1758
  }
1500
1759
 
1501
- // Given proposal (should have locally), ensure returned txs are valid subset and match request indices
1502
- const proposal = await this.mempools.attestationPool.getBlockProposal(request.archiveRoot.toString());
1503
- if (proposal) {
1504
- // Build intersected indices
1505
- const intersectIdx = request.txIndices.getTrueIndices().filter(i => response.txIndices.isSet(i));
1506
-
1507
- // Enforce subset membership and preserve increasing order by index.
1508
- const hashToIndexInProposal = new Map<string, number>(
1509
- proposal.txHashes.map((h, i) => [h.toString(), i] as [string, number]),
1510
- );
1511
- const allowedIndexSet = new Set(intersectIdx);
1512
- const indices = returnedHashes.map(h => hashToIndexInProposal.get(h));
1513
- const allAllowed = indices.every(idx => idx !== undefined && allowedIndexSet.has(idx));
1514
- const strictlyIncreasing = indices.every((idx, i) => (i === 0 ? idx !== undefined : idx! > indices[i - 1]!));
1515
- if (!allAllowed || !strictlyIncreasing) {
1516
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1517
- throw new ValidationError('Returned txs do not match expected subset/order for requested indices');
1518
- }
1519
- } else {
1520
- // No local proposal, cannot check the membership/order of the returned txs
1521
- this.logger.warn(
1522
- `Block proposal not found for archive root ${request.archiveRoot.toString()}; cannot validate membership/order of returned txs`,
1523
- );
1524
- 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');
1525
1769
  }
1526
1770
 
1527
- await Promise.all(response.txs.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator)));
1528
1771
  return true;
1529
1772
  } catch (e: any) {
1530
1773
  if (e instanceof ValidationError) {
@@ -1537,125 +1780,8 @@ export class LibP2PService extends WithTracer implements P2PService {
1537
1780
  }
1538
1781
  }
1539
1782
 
1540
- /**
1541
- * Validate a collection of txs that has been requested from a peer.
1542
- *
1543
- * The core component of this validator is that each tx hash MUST match the requested tx hash,
1544
- * In order to perform this check, the tx proof must be verified.
1545
- *
1546
- * Note: This function is called from within `ReqResp.sendRequest` as part of the
1547
- * ReqRespSubProtocol.TX subprotocol validation.
1548
- *
1549
- * @param requestedTxHash - The collection of the txs that was requested.
1550
- * @param responseTx - The collection of txs that was received as a response to the request.
1551
- * @param peerId - The peer ID of the peer that sent the tx.
1552
- * @returns True if the whole collection of txs is valid, false otherwise.
1553
- */
1554
- @trackSpan('Libp2pService.validateRequestedTx', (requestedTxHash, _responseTx) => ({
1555
- [Attributes.TX_HASH]: requestedTxHash.toString(),
1556
- }))
1557
- private async validateRequestedTxs(requestedTxHash: TxHash[], responseTx: Tx[], peerId: PeerId): Promise<boolean> {
1558
- const requested = new Set(requestedTxHash.map(h => h.toString()));
1559
- const requestedTxValidator = this.createRequestedTxValidator();
1560
-
1561
- //TODO: (mralj) - this is somewhat naive implementation, if single tx is invalid we consider the whole response invalid.
1562
- // I think we should still extract the valid txs and return them, so that we can still use the response.
1563
- try {
1564
- await Promise.all(responseTx.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator, requested)));
1565
- return true;
1566
- } catch (e: any) {
1567
- if (e instanceof ValidationError) {
1568
- this.logger.warn(`Failed to validate requested txs from peer ${peerId.toString()}, reason ${e.message}`);
1569
- } else {
1570
- this.logger.error(`Error during validation of requested txs`, e);
1571
- }
1572
-
1573
- return false;
1574
- }
1575
- }
1576
-
1577
- /**
1578
- * Validates a BLOCK response.
1579
- *
1580
- * If a local copy exists, enforces hash equality. If missing, rejects (no penalty) since the hash cannot be verified.
1581
- * Penalizes on block number mismatch or hash mismatch.
1582
- *
1583
- * @param requestedBlockNumber - The requested block number.
1584
- * @param responseBlock - The block returned by the peer.
1585
- * @param peerId - The peer that returned the block.
1586
- * @returns True if the response is valid, false otherwise.
1587
- */
1588
- @trackSpan('Libp2pService.validateRequestedBlock', (requestedBlockNumber, _responseBlock) => ({
1589
- [Attributes.BLOCK_NUMBER]: requestedBlockNumber.toString(),
1590
- }))
1591
- protected async validateRequestedBlock(
1592
- requestedBlockNumber: Fr,
1593
- responseBlock: L2Block,
1594
- peerId: PeerId,
1595
- ): Promise<boolean> {
1596
- try {
1597
- const reqNum = Number(requestedBlockNumber.toString());
1598
- if (responseBlock.number !== reqNum) {
1599
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1600
- return false;
1601
- }
1602
-
1603
- const local = await this.archiver.getBlock(BlockNumber(reqNum));
1604
- if (!local) {
1605
- // We are missing the local block; we cannot verify the hash yet. Reject without penalizing.
1606
- // TODO: Consider extending this validator to accept an expected hash or
1607
- // performing quorum-based checks when using P2P syncing prior to L1 sync.
1608
- this.logger.warn(`Local block ${reqNum} not found; rejecting BLOCK response without hash verification`);
1609
- return false;
1610
- }
1611
- const [localHash, respHash] = await Promise.all([local.hash(), responseBlock.hash()]);
1612
- if (!localHash.equals(respHash)) {
1613
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1614
- return false;
1615
- }
1616
-
1617
- return true;
1618
- } catch (e) {
1619
- this.logger.warn(`Error validating requested block`, e);
1620
- return false;
1621
- }
1622
- }
1623
-
1624
- protected async validateRequestedTx(
1625
- tx: Tx,
1626
- peerId: PeerId,
1627
- txValidator: TxValidator,
1628
- requested?: Set<`0x${string}`>,
1629
- ) {
1630
- const penalize = (severity: PeerErrorSeverity) => this.peerManager.penalizePeer(peerId, severity);
1631
- if (requested && !requested.has(tx.getTxHash().toString())) {
1632
- penalize(PeerErrorSeverity.MidToleranceError);
1633
- throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that was not requested.`);
1634
- }
1635
-
1636
- const { result } = await txValidator.validateTx(tx);
1637
- if (result === 'invalid') {
1638
- penalize(PeerErrorSeverity.LowToleranceError);
1639
- throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that is invalid.`);
1640
- }
1641
- }
1642
-
1643
- protected createRequestedTxValidator(): TxValidator {
1644
- return createTxValidatorForReqResponseReceivedTxs(this.proofVerifier, {
1645
- l1ChainId: this.config.l1ChainId,
1646
- rollupVersion: this.config.rollupVersion,
1647
- });
1648
- }
1649
-
1650
- private async getGasFees(blockNumber: BlockNumber): Promise<GasFees> {
1651
- if (blockNumber === this.feesCache?.blockNumber) {
1652
- return this.feesCache.gasFees;
1653
- }
1654
-
1655
- const header = await this.archiver.getBlockHeader(blockNumber);
1656
- const gasFees = header?.globalVariables.gasFees ?? GasFees.empty();
1657
- this.feesCache = { blockNumber, gasFees };
1658
- return gasFees;
1783
+ private getGasFees(): Promise<GasFees> {
1784
+ return this.blockMinFeesProvider.getCurrentMinFees();
1659
1785
  }
1660
1786
 
1661
1787
  /**
@@ -1669,8 +1795,10 @@ export class LibP2PService extends WithTracer implements P2PService {
1669
1795
  l1ChainId: this.config.l1ChainId,
1670
1796
  rollupVersion: this.config.rollupVersion,
1671
1797
  proofVerifier: this.proofVerifier,
1798
+ txValidationCache: this.txValidationCache,
1672
1799
  },
1673
1800
  peerScoring: this.peerManager,
1801
+ validateRequestedBlockTxsConsistency: this.validateRequestedBlockTxsConsistency.bind(this),
1674
1802
  };
1675
1803
  }
1676
1804
 
@@ -1679,16 +1807,19 @@ export class LibP2PService extends WithTracer implements P2PService {
1679
1807
  this.proofVerifier,
1680
1808
  { l1ChainId: this.config.l1ChainId, rollupVersion: this.config.rollupVersion },
1681
1809
  this.logger.getBindings(),
1810
+ this.txValidationCache,
1682
1811
  );
1683
1812
 
1684
1813
  const results = await Promise.all(
1685
- txs.map(async tx => {
1686
- const result = await validator.validateTx(tx);
1687
- return result.result !== 'invalid';
1688
- }),
1814
+ txs.map(async tx => ({ txHash: tx.getTxHash(), result: await validator.validateTx(tx) })),
1689
1815
  );
1690
- if (results.some(value => value === false)) {
1691
- 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);
1692
1823
  }
1693
1824
  }
1694
1825
 
@@ -1697,13 +1828,14 @@ export class LibP2PService extends WithTracer implements P2PService {
1697
1828
  currentBlockNumber: BlockNumber,
1698
1829
  nextSlotTimestamp: UInt64,
1699
1830
  ): Promise<Record<string, TransactionValidator>> {
1700
- const gasFees = await this.getGasFees(currentBlockNumber);
1831
+ const gasFees = await this.getGasFees();
1701
1832
  const allowedInSetup = [
1702
1833
  ...(await getDefaultAllowedSetupFunctions()),
1703
1834
  ...(this.config.txPublicSetupAllowListExtend ?? []),
1704
1835
  ];
1705
1836
  const blockNumber = BlockNumber(currentBlockNumber + 1);
1706
1837
  const l1Constants = await this.archiver.getL1Constants();
1838
+ const networkTxGasLimits = getNetworkTxGasLimits(this.config, l1Constants);
1707
1839
 
1708
1840
  return createFirstStageTxValidationsForGossipedTransactions(
1709
1841
  nextSlotTimestamp,
@@ -1718,9 +1850,8 @@ export class LibP2PService extends WithTracer implements P2PService {
1718
1850
  allowedInSetup,
1719
1851
  this.logger.getBindings(),
1720
1852
  {
1721
- rollupManaLimit: l1Constants.rollupManaLimit,
1722
- maxBlockL2Gas: this.config.validateMaxL2BlockGas,
1723
- maxBlockDAGas: this.config.validateMaxDABlockGas,
1853
+ maxTxL2Gas: networkTxGasLimits.l2Gas,
1854
+ maxTxDAGas: networkTxGasLimits.daGas,
1724
1855
  },
1725
1856
  );
1726
1857
  }