@aztec/p2p 0.0.1-commit.9ef841308 → 0.0.1-commit.a4600f49

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 (344) 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 +33 -24
  8. package/dest/client/interface.d.ts +48 -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 +105 -46
  13. package/dest/config.d.ts +143 -107
  14. package/dest/config.d.ts.map +1 -1
  15. package/dest/config.js +72 -26
  16. package/dest/errors/p2p-service.error.d.ts +9 -0
  17. package/dest/errors/p2p-service.error.d.ts.map +1 -0
  18. package/dest/errors/p2p-service.error.js +10 -0
  19. package/dest/errors/reqresp.error.d.ts +9 -16
  20. package/dest/errors/reqresp.error.d.ts.map +1 -1
  21. package/dest/errors/reqresp.error.js +10 -17
  22. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +98 -56
  23. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  24. package/dest/mem_pools/attestation_pool/attestation_pool.js +269 -196
  25. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +1 -1
  26. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  27. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +218 -59
  28. package/dest/mem_pools/attestation_pool/mocks.d.ts +1 -1
  29. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  30. package/dest/mem_pools/attestation_pool/mocks.js +6 -4
  31. package/dest/mem_pools/instrumentation.d.ts +9 -4
  32. package/dest/mem_pools/instrumentation.d.ts.map +1 -1
  33. package/dest/mem_pools/instrumentation.js +28 -9
  34. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts +1 -5
  35. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts.map +1 -1
  36. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.js +1 -7
  37. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +3 -3
  38. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -1
  39. package/dest/mem_pools/tx_pool_v2/deleted_pool.js +6 -6
  40. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +1 -1
  41. package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts +2 -1
  42. package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts.map +1 -1
  43. package/dest/mem_pools/tx_pool_v2/eviction/index.js +1 -0
  44. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts +16 -0
  45. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts.map +1 -0
  46. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.js +62 -0
  47. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +2 -2
  48. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +33 -5
  49. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
  50. package/dest/mem_pools/tx_pool_v2/interfaces.js +2 -1
  51. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +5 -2
  52. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
  53. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +8 -5
  54. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +15 -1
  55. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -1
  56. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +31 -0
  57. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +9 -3
  58. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
  59. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +19 -8
  60. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +17 -4
  61. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
  62. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +208 -118
  63. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +10 -3
  64. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
  65. package/dest/msg_validators/attestation_validator/attestation_validator.js +38 -16
  66. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +7 -3
  67. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
  68. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +4 -5
  69. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +8 -3
  70. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
  71. package/dest/msg_validators/proposal_validator/block_proposal_validator.js +2 -2
  72. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +8 -3
  73. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
  74. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +8 -2
  75. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +25 -3
  76. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
  77. package/dest/msg_validators/proposal_validator/proposal_validator.js +85 -15
  78. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +1 -1
  79. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
  80. package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +8 -15
  81. package/dest/msg_validators/tx_validator/allowed_public_setup.js +3 -3
  82. package/dest/msg_validators/tx_validator/archive_cache.js +1 -1
  83. package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts +15 -0
  84. package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts.map +1 -0
  85. package/dest/msg_validators/tx_validator/cached_tx_validator.js +19 -0
  86. package/dest/msg_validators/tx_validator/data_validator.d.ts +2 -1
  87. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
  88. package/dest/msg_validators/tx_validator/data_validator.js +1 -0
  89. package/dest/msg_validators/tx_validator/factory.d.ts +14 -15
  90. package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
  91. package/dest/msg_validators/tx_validator/factory.js +29 -16
  92. package/dest/msg_validators/tx_validator/gas_validator.d.ts +47 -10
  93. package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
  94. package/dest/msg_validators/tx_validator/gas_validator.js +65 -48
  95. package/dest/msg_validators/tx_validator/index.d.ts +3 -1
  96. package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
  97. package/dest/msg_validators/tx_validator/index.js +2 -0
  98. package/dest/msg_validators/tx_validator/phases_validator.js +2 -2
  99. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +2 -1
  100. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -1
  101. package/dest/msg_validators/tx_validator/tx_proof_validator.js +2 -0
  102. package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts +48 -0
  103. package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts.map +1 -0
  104. package/dest/msg_validators/tx_validator/tx_validation_cache.js +68 -0
  105. package/dest/services/data_store.d.ts +1 -1
  106. package/dest/services/data_store.d.ts.map +1 -1
  107. package/dest/services/data_store.js +5 -5
  108. package/dest/services/discv5/discV5_service.d.ts +13 -2
  109. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  110. package/dest/services/discv5/discV5_service.js +119 -25
  111. package/dest/services/discv5/persisted_enr_store.d.ts +38 -0
  112. package/dest/services/discv5/persisted_enr_store.d.ts.map +1 -0
  113. package/dest/services/discv5/persisted_enr_store.js +145 -0
  114. package/dest/services/dummy_service.d.ts +15 -15
  115. package/dest/services/dummy_service.d.ts.map +1 -1
  116. package/dest/services/dummy_service.js +15 -16
  117. package/dest/services/encoding.d.ts +19 -6
  118. package/dest/services/encoding.d.ts.map +1 -1
  119. package/dest/services/encoding.js +25 -15
  120. package/dest/services/gossipsub/topic_score_params.d.ts +6 -12
  121. package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -1
  122. package/dest/services/gossipsub/topic_score_params.js +4 -14
  123. package/dest/services/libp2p/instrumentation.d.ts +3 -1
  124. package/dest/services/libp2p/instrumentation.d.ts.map +1 -1
  125. package/dest/services/libp2p/instrumentation.js +14 -0
  126. package/dest/services/libp2p/libp2p_service.d.ts +40 -40
  127. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  128. package/dest/services/libp2p/libp2p_service.js +292 -264
  129. package/dest/services/peer-manager/peer_manager.d.ts +15 -2
  130. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  131. package/dest/services/peer-manager/peer_manager.js +59 -17
  132. package/dest/services/peer-manager/peer_scoring.d.ts +30 -1
  133. package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
  134. package/dest/services/peer-manager/peer_scoring.js +74 -3
  135. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +1 -8
  136. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
  137. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +35 -44
  138. package/dest/services/reqresp/batch-tx-requester/interface.d.ts +8 -5
  139. package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -1
  140. package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts +5 -14
  141. package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts.map +1 -1
  142. package/dest/services/reqresp/batch-tx-requester/tx_validator.js +6 -20
  143. package/dest/services/reqresp/config.d.ts +15 -3
  144. package/dest/services/reqresp/config.d.ts.map +1 -1
  145. package/dest/services/reqresp/config.js +11 -0
  146. package/dest/services/reqresp/interface.d.ts +16 -18
  147. package/dest/services/reqresp/interface.d.ts.map +1 -1
  148. package/dest/services/reqresp/interface.js +10 -20
  149. package/dest/services/reqresp/metrics.d.ts +1 -1
  150. package/dest/services/reqresp/metrics.d.ts.map +1 -1
  151. package/dest/services/reqresp/metrics.js +0 -1
  152. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +4 -3
  153. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -1
  154. package/dest/services/reqresp/protocols/block_txs/bitvector.js +3 -2
  155. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +1 -1
  156. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
  157. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +31 -28
  158. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +12 -7
  159. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
  160. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +42 -24
  161. package/dest/services/reqresp/protocols/index.d.ts +1 -2
  162. package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
  163. package/dest/services/reqresp/protocols/index.js +0 -1
  164. package/dest/services/reqresp/protocols/tx.d.ts +1 -1
  165. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
  166. package/dest/services/reqresp/protocols/tx.js +1 -3
  167. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
  168. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
  169. package/dest/services/reqresp/rate-limiter/rate_limits.js +3 -13
  170. package/dest/services/reqresp/reqresp.d.ts +7 -29
  171. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  172. package/dest/services/reqresp/reqresp.js +59 -209
  173. package/dest/services/service.d.ts +23 -16
  174. package/dest/services/service.d.ts.map +1 -1
  175. package/dest/services/tx_collection/config.d.ts +2 -23
  176. package/dest/services/tx_collection/config.d.ts.map +1 -1
  177. package/dest/services/tx_collection/config.js +2 -55
  178. package/dest/services/tx_collection/file_store_tx_collection.d.ts +12 -28
  179. package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -1
  180. package/dest/services/tx_collection/file_store_tx_collection.js +43 -83
  181. package/dest/services/tx_collection/index.d.ts +2 -3
  182. package/dest/services/tx_collection/index.d.ts.map +1 -1
  183. package/dest/services/tx_collection/index.js +0 -1
  184. package/dest/services/tx_collection/instrumentation.d.ts +1 -1
  185. package/dest/services/tx_collection/instrumentation.d.ts.map +1 -1
  186. package/dest/services/tx_collection/instrumentation.js +0 -2
  187. package/dest/services/tx_collection/tx_collection.d.ts +35 -51
  188. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
  189. package/dest/services/tx_collection/tx_collection.js +275 -119
  190. package/dest/services/tx_collection/tx_collection_sink.d.ts +1 -1
  191. package/dest/services/tx_collection/tx_collection_sink.js +2 -2
  192. package/dest/services/tx_collection/tx_source.d.ts +1 -1
  193. package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
  194. package/dest/services/tx_collection/tx_source.js +4 -1
  195. package/dest/services/tx_file_store/tx_file_store.d.ts +1 -3
  196. package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -1
  197. package/dest/services/tx_file_store/tx_file_store.js +4 -14
  198. package/dest/services/tx_provider.d.ts +3 -1
  199. package/dest/services/tx_provider.d.ts.map +1 -1
  200. package/dest/services/tx_provider.js +3 -0
  201. package/dest/test-helpers/make-test-p2p-clients.d.ts +1 -1
  202. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  203. package/dest/test-helpers/make-test-p2p-clients.js +5 -2
  204. package/dest/test-helpers/mock-pubsub.d.ts +21 -12
  205. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  206. package/dest/test-helpers/mock-pubsub.js +39 -47
  207. package/dest/test-helpers/reqresp-nodes.d.ts +4 -5
  208. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  209. package/dest/test-helpers/reqresp-nodes.js +16 -18
  210. package/dest/test-helpers/test_tx_provider.d.ts +3 -1
  211. package/dest/test-helpers/test_tx_provider.d.ts.map +1 -1
  212. package/dest/test-helpers/test_tx_provider.js +3 -0
  213. package/dest/test-helpers/testbench-utils.d.ts +15 -14
  214. package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
  215. package/dest/test-helpers/testbench-utils.js +28 -13
  216. package/dest/testbench/p2p_client_testbench_worker.d.ts +3 -5
  217. package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -1
  218. package/dest/testbench/p2p_client_testbench_worker.js +60 -27
  219. package/dest/testbench/worker_client_manager.d.ts +10 -6
  220. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  221. package/dest/testbench/worker_client_manager.js +51 -8
  222. package/dest/util.d.ts +8 -8
  223. package/dest/util.d.ts.map +1 -1
  224. package/dest/util.js +38 -10
  225. package/dest/versioning.d.ts +3 -6
  226. package/dest/versioning.d.ts.map +1 -1
  227. package/dest/versioning.js +3 -24
  228. package/package.json +16 -16
  229. package/src/bootstrap/bootstrap.ts +17 -3
  230. package/src/client/factory.ts +49 -25
  231. package/src/client/interface.ts +44 -17
  232. package/src/client/p2p_client.ts +125 -79
  233. package/src/client/test/{tx_proposal_collector/README.md → p2p_client.batch_tx_requester.bench.README.md} +23 -53
  234. package/src/config.ts +146 -34
  235. package/src/errors/p2p-service.error.ts +11 -0
  236. package/src/errors/reqresp.error.ts +13 -19
  237. package/src/mem_pools/attestation_pool/attestation_pool.ts +324 -239
  238. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +238 -63
  239. package/src/mem_pools/attestation_pool/mocks.ts +13 -8
  240. package/src/mem_pools/instrumentation.ts +18 -12
  241. package/src/mem_pools/tx_pool_v2/archive/tx_archive.ts +1 -9
  242. package/src/mem_pools/tx_pool_v2/deleted_pool.ts +7 -7
  243. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +1 -1
  244. package/src/mem_pools/tx_pool_v2/eviction/index.ts +1 -0
  245. package/src/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.ts +65 -0
  246. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +3 -3
  247. package/src/mem_pools/tx_pool_v2/interfaces.ts +31 -4
  248. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +13 -7
  249. package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +35 -0
  250. package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +22 -8
  251. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +233 -123
  252. package/src/msg_validators/attestation_validator/attestation_validator.ts +46 -15
  253. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +15 -7
  254. package/src/msg_validators/proposal_validator/block_proposal_validator.ts +15 -3
  255. package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +26 -3
  256. package/src/msg_validators/proposal_validator/proposal_validator.ts +94 -12
  257. package/src/msg_validators/tx_validator/README.md +13 -3
  258. package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +3 -12
  259. package/src/msg_validators/tx_validator/allowed_public_setup.ts +3 -3
  260. package/src/msg_validators/tx_validator/archive_cache.ts +1 -1
  261. package/src/msg_validators/tx_validator/cached_tx_validator.ts +31 -0
  262. package/src/msg_validators/tx_validator/data_validator.ts +2 -0
  263. package/src/msg_validators/tx_validator/factory.ts +37 -20
  264. package/src/msg_validators/tx_validator/gas_validator.ts +99 -54
  265. package/src/msg_validators/tx_validator/index.ts +2 -0
  266. package/src/msg_validators/tx_validator/phases_validator.ts +2 -2
  267. package/src/msg_validators/tx_validator/tx_proof_validator.ts +2 -0
  268. package/src/msg_validators/tx_validator/tx_validation_cache.ts +101 -0
  269. package/src/services/data_store.ts +5 -13
  270. package/src/services/discv5/discV5_service.ts +126 -16
  271. package/src/services/discv5/persisted_enr_store.ts +158 -0
  272. package/src/services/dummy_service.ts +20 -39
  273. package/src/services/encoding.ts +28 -20
  274. package/src/services/gossipsub/README.md +33 -11
  275. package/src/services/gossipsub/topic_score_params.ts +9 -18
  276. package/src/services/libp2p/instrumentation.ts +14 -0
  277. package/src/services/libp2p/libp2p_service.ts +342 -290
  278. package/src/services/peer-manager/peer_manager.ts +63 -16
  279. package/src/services/peer-manager/peer_scoring.ts +81 -3
  280. package/src/services/reqresp/README.md +2 -37
  281. package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +39 -52
  282. package/src/services/reqresp/batch-tx-requester/interface.ts +11 -4
  283. package/src/services/reqresp/batch-tx-requester/tx_validator.ts +12 -25
  284. package/src/services/reqresp/config.ts +19 -2
  285. package/src/services/reqresp/interface.ts +21 -47
  286. package/src/services/reqresp/metrics.ts +0 -1
  287. package/src/services/reqresp/protocols/block_txs/bitvector.ts +3 -2
  288. package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +34 -30
  289. package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +47 -20
  290. package/src/services/reqresp/protocols/index.ts +0 -1
  291. package/src/services/reqresp/protocols/tx.ts +1 -3
  292. package/src/services/reqresp/rate-limiter/rate_limits.ts +3 -13
  293. package/src/services/reqresp/reqresp.ts +69 -252
  294. package/src/services/service.ts +28 -32
  295. package/src/services/tx_collection/config.ts +3 -80
  296. package/src/services/tx_collection/file_store_tx_collection.ts +54 -103
  297. package/src/services/tx_collection/index.ts +1 -6
  298. package/src/services/tx_collection/instrumentation.ts +1 -7
  299. package/src/services/tx_collection/tx_collection.ts +330 -173
  300. package/src/services/tx_collection/tx_collection_sink.ts +2 -2
  301. package/src/services/tx_collection/tx_source.ts +2 -1
  302. package/src/services/tx_file_store/tx_file_store.ts +5 -17
  303. package/src/services/tx_provider.ts +5 -0
  304. package/src/test-helpers/make-test-p2p-clients.ts +3 -0
  305. package/src/test-helpers/mock-pubsub.ts +41 -64
  306. package/src/test-helpers/reqresp-nodes.ts +12 -22
  307. package/src/test-helpers/test_tx_provider.ts +5 -0
  308. package/src/test-helpers/testbench-utils.ts +35 -26
  309. package/src/testbench/p2p_client_testbench_worker.ts +68 -44
  310. package/src/testbench/worker_client_manager.ts +59 -19
  311. package/src/util.ts +33 -14
  312. package/src/versioning.ts +3 -33
  313. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts +0 -2
  314. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts.map +0 -1
  315. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +0 -315
  316. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts +0 -73
  317. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts.map +0 -1
  318. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.js +0 -8
  319. package/dest/msg_validators/clock_tolerance.d.ts +0 -21
  320. package/dest/msg_validators/clock_tolerance.d.ts.map +0 -1
  321. package/dest/msg_validators/clock_tolerance.js +0 -38
  322. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +0 -64
  323. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +0 -1
  324. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.js +0 -151
  325. package/dest/services/reqresp/protocols/block.d.ts +0 -9
  326. package/dest/services/reqresp/protocols/block.d.ts.map +0 -1
  327. package/dest/services/reqresp/protocols/block.js +0 -32
  328. package/dest/services/tx_collection/fast_tx_collection.d.ts +0 -51
  329. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +0 -1
  330. package/dest/services/tx_collection/fast_tx_collection.js +0 -311
  331. package/dest/services/tx_collection/proposal_tx_collector.d.ts +0 -48
  332. package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +0 -1
  333. package/dest/services/tx_collection/proposal_tx_collector.js +0 -50
  334. package/dest/services/tx_collection/slow_tx_collection.d.ts +0 -57
  335. package/dest/services/tx_collection/slow_tx_collection.d.ts.map +0 -1
  336. package/dest/services/tx_collection/slow_tx_collection.js +0 -211
  337. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +0 -355
  338. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.ts +0 -43
  339. package/src/msg_validators/clock_tolerance.ts +0 -52
  340. package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +0 -161
  341. package/src/services/reqresp/protocols/block.ts +0 -37
  342. package/src/services/tx_collection/fast_tx_collection.ts +0 -361
  343. package/src/services/tx_collection/proposal_tx_collector.ts +0 -108
  344. package/src/services/tx_collection/slow_tx_collection.ts +0 -266
@@ -370,15 +370,17 @@ function applyDecs2203RFactory() {
370
370
  function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) {
371
371
  return (_apply_decs_2203_r = applyDecs2203RFactory())(targetClass, memberDecs, classDecs, parentClass);
372
372
  }
373
- var _dec, _dec1, _dec2, _dec3, _dec4, _dec5, _dec6, _dec7, _initProto;
373
+ var _dec, _dec1, _dec2, _dec3, _dec4, _dec5, _initProto;
374
374
  import { BlockNumber } from '@aztec/foundation/branded-types';
375
- import { maxBy } from '@aztec/foundation/collection';
375
+ import { compactArray, maxBy, merge } from '@aztec/foundation/collection';
376
376
  import { createLibp2pComponentLogger, createLogger } from '@aztec/foundation/log';
377
377
  import { RunningPromise } from '@aztec/foundation/running-promise';
378
378
  import { Timer } from '@aztec/foundation/timer';
379
379
  import { protocolContractsHash } from '@aztec/protocol-contracts';
380
- import { GasFees } from '@aztec/stdlib/gas';
380
+ import { DEFAULT_MAX_BLOCKS_PER_CHECKPOINT } from '@aztec/stdlib/config';
381
+ import { getNetworkTxGasLimits } from '@aztec/stdlib/gas';
381
382
  import { BlockProposal, CheckpointAttestation, CheckpointProposal, P2PMessage, PeerErrorSeverity, PeerErrorSeverityByHarshness, TopicType, createTopicString, getTopicsForConfig, metricsTopicStrToLabels } from '@aztec/stdlib/p2p';
383
+ import { ConsensusTimetable, getDefaultCheckpointProposalSyncGrace } from '@aztec/stdlib/timetable';
382
384
  import { MerkleTreeId } from '@aztec/stdlib/trees';
383
385
  import { Tx } from '@aztec/stdlib/tx';
384
386
  import { compressComponentVersions } from '@aztec/stdlib/versioning';
@@ -396,22 +398,36 @@ import { tcp } from '@libp2p/tcp';
396
398
  import { multiaddr } from '@multiformats/multiaddr';
397
399
  import { ENR } from '@nethermindeth/enr';
398
400
  import { createLibp2p } from 'libp2p';
401
+ import { CheckpointProposalReceivedCallbackNotRegisteredError } from '../../errors/p2p-service.error.js';
399
402
  import { BlockProposalValidator, CheckpointAttestationValidator, CheckpointProposalValidator, DoubleSpendTxValidator, FishermanAttestationValidator, getDefaultAllowedSetupFunctions } from '../../msg_validators/index.js';
400
403
  import { MessageSeenValidator } from '../../msg_validators/msg_seen_validator/msg_seen_validator.js';
401
- import { createFirstStageTxValidationsForGossipedTransactions, createSecondStageTxValidationsForGossipedTransactions, createTxValidatorForBlockProposalReceivedTxs, createTxValidatorForReqResponseReceivedTxs } from '../../msg_validators/tx_validator/factory.js';
404
+ import { createFirstStageTxValidationsForGossipedTransactions, createSecondStageTxValidationsForGossipedTransactions, createTxValidatorForBlockProposalReceivedTxs } from '../../msg_validators/tx_validator/factory.js';
402
405
  import { GossipSubEvent } from '../../types/index.js';
403
406
  import { convertToMultiaddr } from '../../util.js';
404
407
  import { getVersions } from '../../versioning.js';
405
408
  import { AztecDatastore } from '../data_store.js';
406
409
  import { DiscV5Service } from '../discv5/discV5_service.js';
407
- import { SnappyTransform, fastMsgIdFn, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
410
+ import { SnappyTransform, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
408
411
  import { APP_SPECIFIC_WEIGHT, gossipScoreThresholds } from '../gossipsub/scoring.js';
409
412
  import { createAllTopicScoreParams } from '../gossipsub/topic_score_params.js';
410
413
  import { PeerManager } from '../peer-manager/peer_manager.js';
411
414
  import { PeerScoring } from '../peer-manager/peer_scoring.js';
412
- import { DEFAULT_SUB_PROTOCOL_VALIDATORS, ReqRespSubProtocol, ValidationError, pingHandler, reqGoodbyeHandler, reqRespBlockHandler, reqRespBlockTxsHandler, reqRespStatusHandler, reqRespTxHandler } from '../reqresp/index.js';
415
+ import { YAMUX_MAX_MESSAGE_SIZE_BYTES } from '../reqresp/config.js';
416
+ import { ReqRespSubProtocol, ValidationError, pingHandler, reqGoodbyeHandler, reqRespBlockTxsHandler, reqRespStatusHandler, reqRespTxHandler } from '../reqresp/index.js';
413
417
  import { ReqResp } from '../reqresp/reqresp.js';
414
418
  import { P2PInstrumentation } from './instrumentation.js';
419
+ /**
420
+ * Builds the {@link ConsensusTimetable} shared by the gossip validators for proposal/attestation receive-window
421
+ * bounds. Derived purely from protocol slot-timing constants plus the block sub-slot duration and the consensus
422
+ * materialization grace, so every node agrees on these bounds without depending on proposer operational budgets.
423
+ */ function buildConsensusTimetable(config, l1Constants) {
424
+ const blockDuration = config.blockDurationMs / 1000;
425
+ return new ConsensusTimetable({
426
+ l1Constants,
427
+ blockDuration,
428
+ checkpointProposalSyncGrace: config.checkpointProposalSyncGraceSeconds ?? getDefaultCheckpointProposalSyncGrace(blockDuration)
429
+ });
430
+ }
415
431
  _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId, attestation)=>({
416
432
  [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString()
417
433
  })), _dec1 = trackSpan('Libp2pService.validateAndStoreBlockProposal', (_peerId, block)=>({
@@ -427,12 +443,8 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
427
443
  [Attributes.SLOT_NUMBER]: checkpoint.slotNumber,
428
444
  [Attributes.BLOCK_ARCHIVE]: checkpoint.archive.toString(),
429
445
  [Attributes.P2P_ID]: await checkpoint.p2pMessageLoggingIdentifier().then((i)=>i.toString())
430
- })), _dec5 = trackSpan('Libp2pService.validateRequestedBlockTxs', (request)=>({
446
+ })), _dec5 = trackSpan('Libp2pService.validateRequestedBlockTxsConsistency', (request)=>({
431
447
  [Attributes.BLOCK_ARCHIVE]: request.archiveRoot.toString()
432
- })), _dec6 = trackSpan('Libp2pService.validateRequestedTx', (requestedTxHash, _responseTx)=>({
433
- [Attributes.TX_HASH]: requestedTxHash.toString()
434
- })), _dec7 = trackSpan('Libp2pService.validateRequestedBlock', (requestedBlockNumber, _responseBlock)=>({
435
- [Attributes.BLOCK_NUMBER]: requestedBlockNumber.toString()
436
448
  }));
437
449
  /**
438
450
  * Lib P2P implementation of the P2PService interface.
@@ -447,6 +459,8 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
447
459
  epochCache;
448
460
  proofVerifier;
449
461
  worldStateSynchronizer;
462
+ blockMinFeesProvider;
463
+ txValidationCache;
450
464
  static{
451
465
  ({ e: [_initProto] } = _apply_decs_2203_r(this, [
452
466
  [
@@ -477,17 +491,7 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
477
491
  [
478
492
  _dec5,
479
493
  2,
480
- "validateRequestedBlockTxs"
481
- ],
482
- [
483
- _dec6,
484
- 2,
485
- "validateRequestedTxs"
486
- ],
487
- [
488
- _dec7,
489
- 2,
490
- "validateRequestedBlock"
494
+ "validateRequestedBlockTxsConsistency"
491
495
  ]
492
496
  ], []));
493
497
  }
@@ -499,9 +503,10 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
499
503
  checkpointAttestationValidator;
500
504
  protocolVersion;
501
505
  topicStrings;
502
- feesCache;
503
506
  /** Callback invoked when a duplicate proposal is detected (triggers slashing). */ duplicateProposalCallback;
507
+ /** Callback invoked when an oversized block proposal is stored as slashing evidence (triggers slashing). */ oversizedProposalCallback;
504
508
  /** Callback invoked when a duplicate attestation is detected (triggers slashing). */ duplicateAttestationCallback;
509
+ /** Callback invoked when a valid checkpoint attestation is accepted into the pool. */ checkpointAttestationCallback;
505
510
  /**
506
511
  * Callback for when a block is received from a peer.
507
512
  * @param block - The block received from the peer.
@@ -511,15 +516,20 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
511
516
  * Callback for when a checkpoint proposal is received from a peer.
512
517
  * @param checkpoint - The checkpoint proposal received from the peer.
513
518
  * @returns The attestations for the checkpoint, if any.
514
- */ checkpointReceivedCallback;
519
+ */ allNodesCheckpointReceivedCallback;
520
+ /**
521
+ * Callback for when a checkpoint proposal is received - specifically for validators - from a peer.
522
+ * @param checkpoint - The checkpoint proposal received from the peer.
523
+ * @returns The attestations for the checkpoint, if any.
524
+ */ validatorCheckpointReceivedCallback;
515
525
  gossipSubEventHandler;
516
526
  ipChangedHandler;
517
- /** Discovered public IP address (set when queryForIp is enabled and no static IP was configured). */ discoveredP2pIp;
527
+ discoveredP2pIp;
518
528
  instrumentation;
519
529
  telemetry;
520
530
  logger;
521
- constructor(config, node, peerDiscoveryService, reqresp, peerManager, mempools, archiver, epochCache, proofVerifier, worldStateSynchronizer, telemetry, logger = createLogger('p2p:libp2p_service')){
522
- super(telemetry, 'LibP2PService'), this.config = config, this.node = node, this.peerDiscoveryService = peerDiscoveryService, this.reqresp = reqresp, this.peerManager = peerManager, this.mempools = mempools, this.archiver = archiver, this.epochCache = epochCache, this.proofVerifier = proofVerifier, this.worldStateSynchronizer = worldStateSynchronizer, this.msgIdSeenValidators = (_initProto(this), {}), this.protocolVersion = '', this.topicStrings = {};
531
+ constructor(config, node, peerDiscoveryService, reqresp, peerManager, mempools, archiver, epochCache, proofVerifier, worldStateSynchronizer, blockMinFeesProvider, telemetry, logger = createLogger('p2p:libp2p_service'), txValidationCache){
532
+ super(telemetry, 'LibP2PService'), this.config = config, this.node = node, this.peerDiscoveryService = peerDiscoveryService, this.reqresp = reqresp, this.peerManager = peerManager, this.mempools = mempools, this.archiver = archiver, this.epochCache = epochCache, this.proofVerifier = proofVerifier, this.worldStateSynchronizer = worldStateSynchronizer, this.blockMinFeesProvider = blockMinFeesProvider, this.txValidationCache = txValidationCache, this.msgIdSeenValidators = (_initProto(this), {}), this.protocolVersion = '', this.topicStrings = {};
523
533
  this.telemetry = telemetry;
524
534
  // Create child logger with fisherman prefix if in fisherman mode
525
535
  this.logger = config.fishermanMode ? logger.createChild('[FISHERMAN]') : logger;
@@ -535,13 +545,27 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
535
545
  this.topicStrings[TopicType.block_proposal] = createTopicString(TopicType.block_proposal, this.protocolVersion);
536
546
  this.topicStrings[TopicType.checkpoint_proposal] = createTopicString(TopicType.checkpoint_proposal, this.protocolVersion);
537
547
  this.topicStrings[TopicType.checkpoint_attestation] = createTopicString(TopicType.checkpoint_attestation, this.protocolVersion);
548
+ // Build the consensus timetable once from protocol slot-timing constants and inject it into every
549
+ // validator so they share one set of receive-window bounds, independent of proposer operational budgets.
550
+ const consensusTimetable = buildConsensusTimetable(config, epochCache.getL1Constants());
538
551
  const proposalValidatorOpts = {
539
552
  txsPermitted: !config.disableTransactions,
540
- maxTxsPerBlock: config.validateMaxTxsPerBlock ?? config.validateMaxTxsPerCheckpoint
553
+ maxTxsPerBlock: config.validateMaxTxsPerBlock ?? config.validateMaxTxsPerCheckpoint,
554
+ maxBlocksPerCheckpoint: config.maxBlocksPerCheckpoint,
555
+ skipSlotValidation: config.skipProposalSlotValidation,
556
+ signatureContext: {
557
+ chainId: config.l1ChainId,
558
+ rollupAddress: config.rollupAddress
559
+ },
560
+ clockDisparityMs: config.maxGossipClockDisparityMs
561
+ };
562
+ this.blockProposalValidator = new BlockProposalValidator(epochCache, consensusTimetable, proposalValidatorOpts);
563
+ this.checkpointProposalValidator = new CheckpointProposalValidator(epochCache, consensusTimetable, proposalValidatorOpts);
564
+ const attestationValidatorOpts = {
565
+ signatureContext: proposalValidatorOpts.signatureContext,
566
+ clockDisparityMs: config.maxGossipClockDisparityMs
541
567
  };
542
- this.blockProposalValidator = new BlockProposalValidator(epochCache, proposalValidatorOpts);
543
- this.checkpointProposalValidator = new CheckpointProposalValidator(epochCache, proposalValidatorOpts);
544
- this.checkpointAttestationValidator = config.fishermanMode ? new FishermanAttestationValidator(epochCache, mempools.attestationPool, telemetry) : new CheckpointAttestationValidator(epochCache);
568
+ this.checkpointAttestationValidator = config.fishermanMode ? new FishermanAttestationValidator(epochCache, consensusTimetable, mempools.attestationPool, telemetry, attestationValidatorOpts) : new CheckpointAttestationValidator(epochCache, consensusTimetable, attestationValidatorOpts);
545
569
  this.gossipSubEventHandler = this.handleGossipSubEvent.bind(this);
546
570
  this.blockReceivedCallback = async (block)=>{
547
571
  this.logger.warn(`Handler for block received not yet registered on P2P service. Received block ${block.blockNumber} for slot ${block.slotNumber} from peer.`, {
@@ -549,13 +573,16 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
549
573
  });
550
574
  return true;
551
575
  };
552
- this.checkpointReceivedCallback = (checkpoint)=>{
553
- this.logger.debug(`Handler not yet registered: Checkpoint received callback not set. Received checkpoint for slot ${checkpoint.slotNumber} from peer.`);
576
+ this.allNodesCheckpointReceivedCallback = (_checkpoint)=>{
577
+ throw new CheckpointProposalReceivedCallbackNotRegisteredError();
578
+ };
579
+ this.validatorCheckpointReceivedCallback = (_checkpoint)=>{
554
580
  return Promise.resolve(undefined);
555
581
  };
556
582
  }
557
583
  updateConfig(config) {
558
584
  this.reqresp.updateConfig(config);
585
+ this.config = merge(this.config, config);
559
586
  }
560
587
  /**
561
588
  * Creates an instance of the LibP2P service.
@@ -563,12 +590,12 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
563
590
  * @param txPool - The transaction pool to be accessed by the service.
564
591
  * @returns The new service.
565
592
  */ static async new(config, peerId, deps) {
566
- const { worldStateSynchronizer, epochCache, l2BlockSource, mempools, proofVerifier, peerStore, telemetry, logger, packageVersion } = deps;
593
+ const { worldStateSynchronizer, epochCache, l2BlockSource, mempools, proofVerifier, peerStore, blockMinFeesProvider, telemetry, logger, packageVersion, txValidationCache } = deps;
567
594
  const { p2pPort, maxPeerCount, listenAddress } = config;
568
595
  const bindAddrTcp = convertToMultiaddr(listenAddress, p2pPort, 'tcp');
569
596
  const datastore = new AztecDatastore(peerStore);
570
597
  const otelMetricsAdapter = new OtelMetricsAdapter(telemetry, logger.getBindings());
571
- const peerDiscoveryService = new DiscV5Service(peerId, config, packageVersion, telemetry, createLogger(`${logger.module}:discv5_service`, logger.getBindings()));
598
+ const peerDiscoveryService = new DiscV5Service(peerId, config, packageVersion, telemetry, createLogger(`${logger.module}:discv5_service`, logger.getBindings()), peerStore);
572
599
  // Seed libp2p's bootstrap discovery with private and trusted peers
573
600
  const bootstrapNodes = [
574
601
  ...config.privatePeers,
@@ -583,31 +610,45 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
583
610
  const versions = getVersions(config);
584
611
  const protocolVersion = compressComponentVersions(versions);
585
612
  const preferredPeersEnrs = config.preferredPeers.map((enr)=>ENR.decodeTxt(enr));
586
- const directPeers = (await Promise.all(preferredPeersEnrs.map(async (enr)=>{
587
- const peerId = await enr.peerId();
588
- const address = enr.getLocationMultiaddr('tcp');
589
- if (address === undefined) {
590
- throw new Error(`Direct peer ${peerId.toString()} has no TCP address, ENR: ${enr.encodeTxt()}`);
613
+ const directPeers = compactArray(await Promise.all(preferredPeersEnrs.map(async (enr)=>{
614
+ try {
615
+ const peerId = await enr.peerId();
616
+ const address = enr.getLocationMultiaddr('tcp');
617
+ if (address === undefined) {
618
+ throw new Error(`Direct peer ${peerId.toString()} has no TCP address, ENR: ${enr.encodeTxt()}`);
619
+ }
620
+ return {
621
+ id: peerId,
622
+ addrs: [
623
+ address
624
+ ]
625
+ };
626
+ } catch (err) {
627
+ // A malformed configured ENR shouldn't abort node setup — skip it and log.
628
+ logger.warn(`Skipping preferred peer with invalid ENR`, {
629
+ err
630
+ });
631
+ return undefined;
591
632
  }
592
- return {
593
- id: peerId,
594
- addrs: [
595
- address
596
- ]
597
- };
598
- }))).filter((peer)=>peer !== undefined);
633
+ })));
599
634
  const announceTcpMultiaddr = config.p2pIp ? [
600
635
  convertToMultiaddr(config.p2pIp, p2pPort, 'tcp')
601
636
  ] : [];
602
- // Create dynamic topic score params based on network configuration
637
+ // Create dynamic topic score params based on network configuration. Scoring uses the network-wide
638
+ // max-blocks-per-checkpoint config value directly to size expected per-slot message rates; these are
639
+ // peer-rate thresholds, not consensus deadlines, so they need no proposer operational budgets.
603
640
  const l1Constants = epochCache.getL1Constants();
604
641
  const topicScoreParams = createAllTopicScoreParams(protocolVersion, {
605
642
  slotDurationMs: l1Constants.slotDuration * 1000,
606
643
  heartbeatIntervalMs: config.gossipsubInterval,
607
644
  targetCommitteeSize: l1Constants.targetCommitteeSize,
608
- blockDurationMs: config.blockDurationMs,
645
+ maxBlocksPerCheckpoint: config.maxBlocksPerCheckpoint ?? DEFAULT_MAX_BLOCKS_PER_CHECKPOINT,
609
646
  expectedBlockProposalsPerSlot: config.expectedBlockProposalsPerSlot
610
647
  });
648
+ // Restrict gossipsub to exactly the topics we subscribe to. Without this, an arbitrary-topic
649
+ // message is transformed, msg-id'd and inserted into the seenCache before the subscription check,
650
+ // so a crafted topic colliding on msg id can suppress a real message as a duplicate.
651
+ const allowedTopics = getTopicsForConfig(config.disableTransactions).map((topic)=>createTopicString(topic, protocolVersion));
611
652
  const node = await createLibp2p({
612
653
  start: false,
613
654
  peerId,
@@ -641,8 +682,11 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
641
682
  ],
642
683
  datastore,
643
684
  peerDiscovery,
685
+ // Pin the yamux frame size: MAX_REQRESP_REQUEST_SIZE_BYTES relies on a reqresp request fitting in one frame.
644
686
  streamMuxers: [
645
- yamux(),
687
+ yamux({
688
+ maxMessageSize: YAMUX_MAX_MESSAGE_SIZE_BYTES
689
+ }),
646
690
  mplex()
647
691
  ],
648
692
  connectionEncryption: [
@@ -660,7 +704,7 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
660
704
  },
661
705
  connectionGater: {
662
706
  denyInboundConnection: (maConn)=>{
663
- const allowed = peerManager.isNodeAllowedToConnect(maConn.remoteAddr.nodeAddress().address);
707
+ const allowed = peerManager.isAddressAllowedToConnect(maConn.remoteAddr.nodeAddress().address);
664
708
  if (allowed) {
665
709
  return false;
666
710
  }
@@ -670,7 +714,7 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
670
714
  denyInboundEncryptedConnection: (peerId, _maConn)=>{
671
715
  //NOTE: it is not necessary to check address here because this was already done by
672
716
  // denyInboundConnection
673
- const allowed = peerManager.isNodeAllowedToConnect(peerId);
717
+ const allowed = peerManager.isPeerAllowedToConnect(peerId);
674
718
  if (allowed) {
675
719
  return false;
676
720
  }
@@ -697,9 +741,13 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
697
741
  mcacheLength: config.gossipsubMcacheLength,
698
742
  mcacheGossip: config.gossipsubMcacheGossip,
699
743
  seenTTL: config.gossipsubSeenTTL,
744
+ allowedTopics,
745
+ // No fastMsgIdFn: the fast-path dedup cache keys on a non-cryptographic 64-bit hash of the
746
+ // raw data only (no topic), so a collision — accidental or engineered via a weak seed — drops
747
+ // a different message with no fallback to the full id. Dedup instead rests solely on the
748
+ // cryptographic, topic-framed msgIdFn below.
700
749
  msgIdFn: getMsgIdFn,
701
750
  msgIdToStrFn: msgIdToStrFn,
702
- fastMsgIdFn: fastMsgIdFn,
703
751
  dataTransform: new SnappyTransform(),
704
752
  metricsRegister: otelMetricsAdapter,
705
753
  metricsTopicStrToLabel: metricsTopicStrToLabels(protocolVersion),
@@ -721,6 +769,8 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
721
769
  const peerScoring = new PeerScoring(config, telemetry);
722
770
  const reqresp = new ReqResp(config, node, peerScoring, createLogger(`${logger.module}:reqresp`));
723
771
  const peerManager = new PeerManager(node, peerDiscoveryService, config, telemetry, createLogger(`${logger.module}:peer_manager`), peerScoring, reqresp, worldStateSynchronizer, protocolVersion, epochCache);
772
+ // Gate req/resp data protocols for unauthenticated peers when p2pAllowOnlyValidators is enabled
773
+ reqresp.setShouldRejectPeer((peerId)=>peerManager.shouldDisableP2PGossip(peerId));
724
774
  // Configure application-specific scoring for gossipsub.
725
775
  // The weight scales app score to align with gossipsub thresholds:
726
776
  // - Disconnect (-50) × 10 = -500 = gossipThreshold (stops receiving gossip)
@@ -728,7 +778,7 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
728
778
  // Note: positive topic scores can offset penalties, so alignment is best-effort.
729
779
  node.services.pubsub.score.params.appSpecificWeight = APP_SPECIFIC_WEIGHT;
730
780
  node.services.pubsub.score.params.appSpecificScore = (peerId)=>peerManager.shouldDisableP2PGossip(peerId) ? -Infinity : peerManager.getPeerScore(peerId);
731
- return new LibP2PService(config, node, peerDiscoveryService, reqresp, peerManager, mempools, l2BlockSource, epochCache, proofVerifier, worldStateSynchronizer, telemetry, logger);
781
+ return new LibP2PService(config, node, peerDiscoveryService, reqresp, peerManager, mempools, l2BlockSource, epochCache, proofVerifier, worldStateSynchronizer, blockMinFeesProvider, telemetry, logger, txValidationCache);
732
782
  }
733
783
  /**
734
784
  * Starts the LibP2P service.
@@ -738,22 +788,19 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
738
788
  if (this.node.status === 'started') {
739
789
  throw new Error('P2P service already started');
740
790
  }
741
- // Get listen & announce addresses for logging
742
791
  const { p2pIp, p2pPort } = this.config;
743
792
  if (!p2pIp && !this.config.queryForIp) {
744
- throw new Error('Announce address not provided.');
793
+ throw new Error('Announce address not provided and queryForIp is not enabled.');
745
794
  }
746
795
  const announceTcpMultiaddr = p2pIp ? convertToMultiaddr(p2pIp, p2pPort, 'tcp') : undefined;
747
796
  // Create request response protocol handlers
748
797
  const txHandler = reqRespTxHandler(this.mempools);
749
798
  const goodbyeHandler = reqGoodbyeHandler(this.peerManager);
750
- const blockHandler = reqRespBlockHandler(this.archiver);
751
799
  const statusHandler = reqRespStatusHandler(this.protocolVersion, this.worldStateSynchronizer, this.logger);
752
800
  const requestResponseHandlers = {
753
801
  [ReqRespSubProtocol.PING]: pingHandler,
754
802
  [ReqRespSubProtocol.STATUS]: statusHandler.bind(this),
755
- [ReqRespSubProtocol.GOODBYE]: goodbyeHandler.bind(this),
756
- [ReqRespSubProtocol.BLOCK]: blockHandler.bind(this)
803
+ [ReqRespSubProtocol.GOODBYE]: goodbyeHandler.bind(this)
757
804
  };
758
805
  if (!this.config.disableTransactions) {
759
806
  const blockTxsHandler = reqRespBlockTxsHandler(this.mempools.attestationPool, this.archiver, this.mempools.txPool);
@@ -762,15 +809,8 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
762
809
  if (!this.config.disableTransactions) {
763
810
  requestResponseHandlers[ReqRespSubProtocol.TX] = txHandler.bind(this);
764
811
  }
765
- // Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
766
- const reqrespSubProtocolValidators = {
767
- ...DEFAULT_SUB_PROTOCOL_VALIDATORS,
768
- [ReqRespSubProtocol.TX]: this.validateRequestedTxs.bind(this),
769
- [ReqRespSubProtocol.BLOCK_TXS]: this.validateRequestedBlockTxs.bind(this),
770
- [ReqRespSubProtocol.BLOCK]: this.validateRequestedBlock.bind(this)
771
- };
772
812
  await this.peerManager.initializePeers();
773
- await this.reqresp.start(requestResponseHandlers, reqrespSubProtocolValidators);
813
+ await this.reqresp.start(requestResponseHandlers);
774
814
  await this.node.start();
775
815
  // Subscribe to standard GossipSub topics by default
776
816
  for (const topic of getTopicsForConfig(this.config.disableTransactions)){
@@ -782,26 +822,31 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
782
822
  if (!this.config.p2pDiscoveryDisabled) {
783
823
  await this.peerDiscoveryService.start();
784
824
  }
785
- // When queryForIp is enabled and no static IP was configured, bridge discv5 IP discovery to libp2p.
786
- // Discv5 discovers our public IP via peer WHOAREYOU exchanges (enrUpdate=true) and emits 'ip:changed'.
787
- // We confirm the discovered address in the libp2p AddressManager so it appears in getMultiaddrs()
788
- // and is pushed to all connected peers via the identify protocol.
789
- if (this.config.queryForIp && !p2pIp) {
825
+ // Bridge discv5 IP changes to libp2p's AddressManager so peers see the updated address
826
+ if (this.config.queryForIp) {
827
+ this.discoveredP2pIp = this.config.p2pIp;
828
+ this.logger.info('IP change tracking enabled, bridging discv5 IP updates to libp2p AddressManager');
790
829
  this.ipChangedHandler = (ip)=>{
791
830
  const addressManager = this.node.services.components.addressManager;
792
831
  const newAddr = multiaddr(convertToMultiaddr(ip, this.config.p2pPort, 'tcp'));
793
- // Remove old discovered IP if one exists
794
- if (this.discoveredP2pIp) {
795
- const oldAddr = multiaddr(convertToMultiaddr(this.discoveredP2pIp, this.config.p2pPort, 'tcp'));
832
+ const previousIp = this.discoveredP2pIp;
833
+ if (previousIp) {
834
+ const oldAddr = multiaddr(convertToMultiaddr(previousIp, this.config.p2pPort, 'tcp'));
796
835
  addressManager.removeObservedAddr(oldAddr);
836
+ this.logger.info('Libp2p announce address updated due to IP change', {
837
+ previousIp,
838
+ newIp: ip,
839
+ newMultiaddr: newAddr.toString()
840
+ });
841
+ } else {
842
+ this.logger.info('Libp2p announce address set from initial discv5 IP discovery', {
843
+ ip,
844
+ multiaddr: newAddr.toString()
845
+ });
797
846
  }
798
847
  addressManager.addObservedAddr(newAddr);
799
848
  addressManager.confirmObservedAddr(newAddr);
800
- // Store discovered IP
801
849
  this.discoveredP2pIp = ip;
802
- this.logger.info('Public IP discovered via discv5', {
803
- ip
804
- });
805
850
  };
806
851
  this.peerDiscoveryService.on('ip:changed', this.ipChangedHandler);
807
852
  }
@@ -812,7 +857,7 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
812
857
  this.logger.info(`Started P2P service`, {
813
858
  listen: this.config.listenAddress,
814
859
  port: this.config.p2pPort,
815
- announce: announceTcpMultiaddr ?? 'pending (queryForIp=true)',
860
+ announce: announceTcpMultiaddr,
816
861
  peerId: this.node.peerId.toString()
817
862
  });
818
863
  }
@@ -822,9 +867,8 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
822
867
  */ async stop() {
823
868
  // Remove gossip sub listener
824
869
  this.node.services.pubsub.removeEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
825
- // Remove ip:changed listener if registered
826
870
  if (this.ipChangedHandler) {
827
- this.peerDiscoveryService.off('ip:changed', this.ipChangedHandler);
871
+ this.peerDiscoveryService.removeListener('ip:changed', this.ipChangedHandler);
828
872
  this.ipChangedHandler = undefined;
829
873
  }
830
874
  // Stop peer manager
@@ -840,8 +884,8 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
840
884
  await this.stopLibP2P();
841
885
  this.logger.info('LibP2P service stopped');
842
886
  }
843
- addReqRespSubProtocol(subProtocol, handler, validator) {
844
- return this.reqresp.addSubProtocol(subProtocol, handler, validator);
887
+ addReqRespSubProtocol(subProtocol, handler) {
888
+ return this.reqresp.addSubProtocol(subProtocol, handler);
845
889
  }
846
890
  registerThisValidatorAddresses(address) {
847
891
  this.peerManager.registerThisValidatorAddresses(address);
@@ -863,14 +907,6 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
863
907
  };
864
908
  setImmediate(()=>void safeJob());
865
909
  }
866
- /**
867
- * Send a batch of requests to peers, and return the responses
868
- * @param protocol - The request response protocol to use
869
- * @param requests - The requests to send to the peers
870
- * @returns The responses to the requests
871
- */ sendBatchRequest(protocol, requests, pinnedPeerId) {
872
- return this.reqresp.sendBatchRequest(protocol, requests, pinnedPeerId);
873
- }
874
910
  sendRequestToPeer(peerId, subProtocol, payload, dialTimeout) {
875
911
  return this.reqresp.sendRequestToPeer(peerId, subProtocol, payload, dialTimeout);
876
912
  }
@@ -883,8 +919,11 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
883
919
  registerBlockReceivedCallback(callback) {
884
920
  this.blockReceivedCallback = callback;
885
921
  }
886
- registerCheckpointReceivedCallback(callback) {
887
- this.checkpointReceivedCallback = callback;
922
+ registerValidatorCheckpointReceivedCallback(callback) {
923
+ this.validatorCheckpointReceivedCallback = callback;
924
+ }
925
+ registerAllNodesCheckpointReceivedCallback(callback) {
926
+ this.allNodesCheckpointReceivedCallback = callback;
888
927
  }
889
928
  /**
890
929
  * Registers a callback to be invoked when a duplicate proposal is detected.
@@ -893,12 +932,20 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
893
932
  this.duplicateProposalCallback = callback;
894
933
  }
895
934
  /**
935
+ * Registers a callback to be invoked when an oversized block proposal is stored as slashing evidence.
936
+ */ registerOversizedProposalCallback(callback) {
937
+ this.oversizedProposalCallback = callback;
938
+ }
939
+ /**
896
940
  * Registers a callback to be invoked when a duplicate attestation is detected.
897
941
  * A validator signing attestations for different proposals at the same slot.
898
942
  * This callback is triggered on the first duplicate (when count goes from 1 to 2).
899
943
  */ registerDuplicateAttestationCallback(callback) {
900
944
  this.duplicateAttestationCallback = callback;
901
945
  }
946
+ registerCheckpointAttestationCallback(callback) {
947
+ this.checkpointAttestationCallback = callback;
948
+ }
902
949
  /**
903
950
  * Subscribes to a topic.
904
951
  * @param topic - The topic to subscribe to.
@@ -1013,6 +1060,13 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
1013
1060
  }
1014
1061
  // Process the message, optionally within a linked span for trace propagation
1015
1062
  const processMessage = async ()=>{
1063
+ if (this.config.skipIncomingProposals && (msg.topic === this.topicStrings[TopicType.block_proposal] || msg.topic === this.topicStrings[TopicType.checkpoint_proposal])) {
1064
+ this.logger.warn(`Ignoring incoming proposal (skipIncomingProposals is set)`, {
1065
+ topic: msg.topic
1066
+ });
1067
+ this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Ignore);
1068
+ return;
1069
+ }
1016
1070
  if (msg.topic === this.topicStrings[TopicType.tx]) {
1017
1071
  await this.handleGossipedTx(p2pMessage.payload, msgId, source);
1018
1072
  } else if (msg.topic === this.topicStrings[TopicType.checkpoint_attestation]) {
@@ -1076,6 +1130,18 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
1076
1130
  topicType
1077
1131
  });
1078
1132
  }
1133
+ const validationTimeMs = timer.ms();
1134
+ const mcacheWindowMs = this.config.gossipsubMcacheLength * this.config.gossipsubInterval;
1135
+ if (validationTimeMs > mcacheWindowMs * 0.75) {
1136
+ this.instrumentation.incSlowValidation(topicType);
1137
+ this.logger.warn(`Gossip validation for ${topicType} took ${validationTimeMs}ms, approaching mcache eviction window of ${mcacheWindowMs}ms. ` + `Message forwarding may be skipped if validation exceeds the window.`, {
1138
+ msgId,
1139
+ source: source.toString(),
1140
+ topicType,
1141
+ validationTimeMs,
1142
+ mcacheWindowMs
1143
+ });
1144
+ }
1079
1145
  if (resultAndObj.result === TopicValidatorResult.Accept) {
1080
1146
  this.logger.debug(`Message ${topicType} accepted by validator`, {
1081
1147
  msgId,
@@ -1310,15 +1376,17 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
1310
1376
  }
1311
1377
  }
1312
1378
  // Attestation was added successfully - accept it so other nodes can also detect the equivocation
1379
+ this.checkpointAttestationCallback?.(attestation);
1313
1380
  return {
1314
1381
  result: TopicValidatorResult.Accept,
1315
1382
  obj: attestation
1316
1383
  };
1317
1384
  }
1318
1385
  async processBlockFromPeer(payloadData, msgId, source) {
1319
- const { result, obj: block, metadata: { isEquivocated } = {} } = await this.validateReceivedMessage(()=>this.validateAndStoreBlockProposal(source, BlockProposal.fromBuffer(payloadData)), msgId, source, TopicType.block_proposal);
1320
- // If not accepted or equivocated, return
1321
- if (result !== TopicValidatorResult.Accept || !block || isEquivocated) {
1386
+ const { result, obj: block, metadata: { isEquivocated, isOversized } = {} } = await this.validateReceivedMessage(()=>this.validateAndStoreBlockProposal(source, BlockProposal.fromBuffer(payloadData)), msgId, source, TopicType.block_proposal);
1387
+ // If not accepted, equivocated, or oversized, return. Oversized proposals are re-broadcast as
1388
+ // slashing evidence but never attested or processed.
1389
+ if (result !== TopicValidatorResult.Accept || !block || isEquivocated || isOversized) {
1322
1390
  return;
1323
1391
  }
1324
1392
  await this.processValidBlockProposal(block, source);
@@ -1341,6 +1409,10 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
1341
1409
  // Try to add the proposal: this handles existence check, cap check, and adding in one call
1342
1410
  const { added, alreadyExists, count } = await this.mempools.attestationPool.tryAddBlockProposal(block);
1343
1411
  const isEquivocated = count !== undefined && count > 1;
1412
+ // An oversized proposal (index at or beyond the consensus per-checkpoint limit) is structurally valid
1413
+ // proposer misbehavior: it is stored and re-broadcast as slashing evidence but never processed or
1414
+ // attested to. No-ops when maxBlocksPerCheckpoint is unset (local/test).
1415
+ const isOversized = this.config.maxBlocksPerCheckpoint !== undefined && block.indexWithinCheckpoint >= this.config.maxBlocksPerCheckpoint;
1344
1416
  // Duplicate proposal received, no need to re-broadcast
1345
1417
  if (alreadyExists) {
1346
1418
  this.logger.debug(`Ignoring duplicate block proposal received`, {
@@ -1353,7 +1425,8 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
1353
1425
  result: TopicValidatorResult.Ignore,
1354
1426
  obj: block,
1355
1427
  metadata: {
1356
- isEquivocated
1428
+ isEquivocated,
1429
+ isOversized
1357
1430
  }
1358
1431
  };
1359
1432
  }
@@ -1369,11 +1442,30 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
1369
1442
  return {
1370
1443
  result: TopicValidatorResult.Reject,
1371
1444
  metadata: {
1372
- isEquivocated
1445
+ isEquivocated,
1446
+ isOversized
1373
1447
  },
1374
1448
  severity: PeerErrorSeverity.HighToleranceError
1375
1449
  };
1376
1450
  }
1451
+ // The proposal was stored: if oversized, invoke the oversized callback so the proposer can be
1452
+ // slashed. Fired alongside (not instead of) equivocation detection below.
1453
+ if (isOversized) {
1454
+ const proposer = block.getSender();
1455
+ if (proposer) {
1456
+ this.logger.warn(`Detected oversized block proposal at slot ${block.slotNumber}`, {
1457
+ ...block.toBlockInfo(),
1458
+ indexWithinCheckpoint: block.indexWithinCheckpoint,
1459
+ maxBlocksPerCheckpoint: this.config.maxBlocksPerCheckpoint,
1460
+ source: peerId.toString(),
1461
+ proposer: proposer.toString()
1462
+ });
1463
+ this.oversizedProposalCallback?.({
1464
+ slot: block.slotNumber,
1465
+ proposer
1466
+ });
1467
+ }
1468
+ }
1377
1469
  // If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
1378
1470
  // and do re-broadcast it so other nodes in the network know to slash the proposer
1379
1471
  if (isEquivocated) {
@@ -1395,14 +1487,19 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
1395
1487
  result: TopicValidatorResult.Accept,
1396
1488
  obj: block,
1397
1489
  metadata: {
1398
- isEquivocated
1490
+ isEquivocated,
1491
+ isOversized
1399
1492
  }
1400
1493
  };
1401
1494
  }
1402
1495
  // Otherwise, we're good to go!
1403
1496
  return {
1404
1497
  result: TopicValidatorResult.Accept,
1405
- obj: block
1498
+ obj: block,
1499
+ metadata: {
1500
+ isEquivocated: false,
1501
+ isOversized
1502
+ }
1406
1503
  };
1407
1504
  }
1408
1505
  // REFACTOR(palla): This method should be moved to the p2p_client or to a separate component,
@@ -1421,22 +1518,35 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
1421
1518
  const isValid = await this.blockReceivedCallback(block, sender);
1422
1519
  if (!isValid) {
1423
1520
  this.logger.info(`Block proposal validation failed for block ${block.blockNumber}`, block.toBlockInfo());
1521
+ // Release the protections this proposal created so its txs return to pending. Only entries still
1522
+ // keyed to this slot are cleared, so a tx referenced by a live proposal at another slot stays protected.
1523
+ await this.mempools.txPool.unprotectTxs(block.txHashes, slot);
1424
1524
  }
1425
1525
  }
1426
1526
  /**
1427
1527
  * Handle a gossiped checkpoint proposal.
1428
1528
  * Validates and processes the checkpoint proposal, then triggers the callback for attestation.
1429
1529
  */ async handleGossipedCheckpointProposal(payloadData, msgId, source) {
1430
- const { result, obj: checkpoint, metadata: { isEquivocated, processBlock } = {} } = await this.validateReceivedMessage(()=>this.validateAndStoreCheckpointProposal(source, CheckpointProposal.fromBuffer(payloadData)), msgId, source, TopicType.checkpoint_proposal);
1530
+ const { result, obj: checkpoint, metadata: { isEquivocated, processBlock, isOversized } = {} } = await this.validateReceivedMessage(()=>this.validateAndStoreCheckpointProposal(source, CheckpointProposal.fromBuffer(payloadData)), msgId, source, TopicType.checkpoint_proposal);
1531
+ // An oversized checkpoint is re-broadcast as slashing evidence but never attested or processed.
1532
+ // Process checkpoint proposal if valid and neither equivocated nor oversized.
1533
+ const processCheckpointFn = ()=>result === TopicValidatorResult.Accept && checkpoint && !isEquivocated && !isOversized ? this.processValidCheckpointProposal(checkpoint.toCore(), source) : Promise.resolve();
1431
1534
  // If the checkpoint contained a valid last block, we process it even if the checkpoint itself is to be rejected
1432
1535
  // TODO(palla/mbps): Is this ok? Should we be considering a block from a checkpoint that was equivocated?
1433
- if (processBlock && checkpoint?.getBlockProposal()) {
1434
- await this.processValidBlockProposal(checkpoint.getBlockProposal(), source);
1435
- }
1436
- if (result !== TopicValidatorResult.Accept || !checkpoint || isEquivocated) {
1536
+ const processBlockFn = ()=>processBlock && checkpoint && checkpoint.getBlockProposal() ? this.processValidBlockProposal(checkpoint.getBlockProposal(), source) : Promise.resolve();
1537
+ // A node that skips checkpoint validation attests without re-executing the embedded last block, so run
1538
+ // the checkpoint callback first: this creates and broadcasts the attestation before the block is
1539
+ // processed. Otherwise the block's re-execution — which can stall until the re-execution deadline
1540
+ // waiting for a parent that may never arrive — would delay the attestation past the slot's attestation
1541
+ // window, after which peers reject it as stale.
1542
+ if (this.config.skipCheckpointProposalValidation) {
1543
+ await processCheckpointFn();
1544
+ await processBlockFn();
1437
1545
  return;
1438
1546
  }
1439
- await this.processValidCheckpointProposal(checkpoint.toCore(), source);
1547
+ // Process the block first, since it's required for the checkpoint proposal validation.
1548
+ await processBlockFn();
1549
+ await processCheckpointFn();
1440
1550
  }
1441
1551
  /**
1442
1552
  * Validates a checkpoint proposal. Penalizes peer if validation fails. Adds the checkpoint and
@@ -1459,13 +1569,15 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
1459
1569
  // Extract and try to add the block proposal first if present
1460
1570
  const blockProposal = checkpoint.getBlockProposal();
1461
1571
  let processBlock = false;
1572
+ let isOversized = false;
1462
1573
  if (blockProposal) {
1463
1574
  this.logger.debug(`Validating block proposal from propagated checkpoint`, {
1464
1575
  [Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
1465
1576
  [Attributes.P2P_ID]: peerId.toString()
1466
1577
  });
1467
1578
  const blockProposalResult = await this.validateAndStoreBlockProposal(peerId, blockProposal);
1468
- const { obj, metadata: { isEquivocated } = {} } = blockProposalResult;
1579
+ const { obj, metadata: { isEquivocated, isOversized: blockIsOversized } = {} } = blockProposalResult;
1580
+ isOversized = blockIsOversized ?? false;
1469
1581
  if (blockProposalResult.result === TopicValidatorResult.Reject || !obj || isEquivocated) {
1470
1582
  this.logger.debug(`Rejecting checkpoint due to invalid last block proposal`, {
1471
1583
  [Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
@@ -1477,7 +1589,8 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
1477
1589
  result: TopicValidatorResult.Reject,
1478
1590
  severity: 'severity' in blockProposalResult ? blockProposalResult.severity : PeerErrorSeverity.MidToleranceError
1479
1591
  };
1480
- } else if (blockProposalResult.result === TopicValidatorResult.Accept && obj && !isEquivocated) {
1592
+ } else if (blockProposalResult.result === TopicValidatorResult.Accept && obj && !isEquivocated && !isOversized) {
1593
+ // An oversized terminal block is re-broadcast as slashing evidence but never processed.
1481
1594
  processBlock = true;
1482
1595
  }
1483
1596
  }
@@ -1497,11 +1610,12 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
1497
1610
  obj: checkpoint,
1498
1611
  metadata: {
1499
1612
  isEquivocated,
1500
- processBlock
1613
+ processBlock,
1614
+ isOversized
1501
1615
  }
1502
1616
  };
1503
1617
  }
1504
- // Too many checkpoint proposals received for this slot, penalize peer and do not re-broadcast
1618
+ // Too many checkpoint proposals received for this slot, penalize peer and do not re-broadcast.
1505
1619
  // Note: We still return the checkpoint obj so the lastBlock can be processed if valid
1506
1620
  if (!added) {
1507
1621
  this.logger.warn(`Penalizing peer for checkpoint proposal exceeding per-slot cap`, {
@@ -1514,7 +1628,8 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
1514
1628
  obj: checkpoint,
1515
1629
  metadata: {
1516
1630
  isEquivocated,
1517
- processBlock
1631
+ processBlock,
1632
+ isOversized
1518
1633
  },
1519
1634
  severity: PeerErrorSeverity.HighToleranceError
1520
1635
  };
@@ -1541,7 +1656,8 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
1541
1656
  obj: checkpoint,
1542
1657
  metadata: {
1543
1658
  isEquivocated,
1544
- processBlock
1659
+ processBlock,
1660
+ isOversized
1545
1661
  }
1546
1662
  };
1547
1663
  }
@@ -1551,7 +1667,8 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
1551
1667
  obj: checkpoint,
1552
1668
  metadata: {
1553
1669
  processBlock,
1554
- isEquivocated
1670
+ isEquivocated,
1671
+ isOversized
1555
1672
  }
1556
1673
  };
1557
1674
  }
@@ -1566,9 +1683,10 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
1566
1683
  archive: checkpoint.archive.toString(),
1567
1684
  source: sender.toString()
1568
1685
  });
1686
+ await this.allNodesCheckpointReceivedCallback(checkpoint, sender);
1569
1687
  // Call the checkpoint received callback with the core version (without lastBlock)
1570
1688
  // to validate and potentially generate attestations
1571
- const attestations = await this.checkpointReceivedCallback(checkpoint, sender);
1689
+ const attestations = await this.validatorCheckpointReceivedCallback(checkpoint, sender);
1572
1690
  if (attestations && attestations.length > 0) {
1573
1691
  // If the callback returned attestations, add them to the pool and propagate them
1574
1692
  await this.mempools.attestationPool.addOwnCheckpointAttestations(attestations);
@@ -1592,62 +1710,66 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
1592
1710
  });
1593
1711
  }
1594
1712
  /**
1595
- * Validate the requested block transactions. Allow partial returns.
1713
+ * Validate the requested block transactions request-response consistency.
1714
+ * It does NOT validate the transactions themselves.
1596
1715
  * @param request - The block transactions request.
1597
1716
  * @param response - The block transactions response.
1598
1717
  * @param peerId - The ID of the peer that made the request.
1599
- * @returns True if the requested block transactions are valid, false otherwise.
1600
- */ async validateRequestedBlockTxs(request, response, peerId) {
1601
- const requestedTxValidator = this.createRequestedTxValidator();
1718
+ * @returns True if the request-response is consistent, false otherwise.
1719
+ */ async validateRequestedBlockTxsConsistency(request, response, peerId) {
1602
1720
  try {
1603
- if (!response.archiveRoot.equals(request.archiveRoot)) {
1604
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1605
- throw new ValidationError(`Received block txs for unexpected archive root: expected ${request.archiveRoot.toString()}, got ${response.archiveRoot.toString()}`);
1606
- }
1607
- if (response.txIndices.getLength() !== request.txIndices.getLength()) {
1608
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1609
- throw new ValidationError(`Received block txs with mismatched bitvector length: expected ${request.txIndices.getLength()}, got ${response.txIndices.getLength()}`);
1610
- }
1611
- // Check no duplicates and not exceeding returnable count
1612
- const requestedIndices = new Set(request.txIndices.getTrueIndices());
1613
- const availableIndices = new Set(response.txIndices.getTrueIndices());
1614
- const maxReturnable = [
1615
- ...requestedIndices
1616
- ].filter((i)=>availableIndices.has(i)).length;
1617
- const returnedHashes = await Promise.all(response.txs.map((tx)=>tx.getTxHash().toString()));
1721
+ // Check for duplicates txs (or hashes) in the response.
1722
+ const returnedHashes = response.txs.map((tx)=>tx.getTxHash().toString());
1618
1723
  const uniqueReturned = new Set(returnedHashes.map((h)=>h.toString()));
1619
1724
  if (uniqueReturned.size !== returnedHashes.length) {
1620
1725
  this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1621
1726
  throw new ValidationError(`Received duplicate txs in block txs response`);
1622
1727
  }
1623
- if (response.txs.length > maxReturnable) {
1728
+ // We get the block tx hashes from the proposal or the archiver.
1729
+ const proposal = await this.mempools.attestationPool.getBlockProposalByArchive(request.archiveRoot.toString());
1730
+ const blockTxHashes = proposal?.txHashes ?? (await this.archiver.getBlock({
1731
+ archive: request.archiveRoot
1732
+ }))?.body.txEffects.map((e)=>e.txHash);
1733
+ // If we don't have the block, we can't verify membership/order of the returned txs.
1734
+ if (!blockTxHashes) {
1735
+ // This shouldn't happen, since we asked for the block (proposal).
1736
+ // Neither a local proposal nor an archived block: we cannot verify membership/order of the
1737
+ // returned txs. This is a local-state gap, not a peer fault, so we do not penalize.
1738
+ this.logger.warn(`Block ${request.archiveRoot.toString()} not found in attestation pool or archiver; cannot validate membership/order of returned txs`);
1739
+ // NOTE: We mark the response as invalid, given the limitations of a true/false return value.
1740
+ return false;
1741
+ }
1742
+ // Verify that the returned tx hashes are a subset of (tx hashes by index) U (explicitly requested tx hashes).
1743
+ const uniqueRequestedHashes = new Set([
1744
+ // tx hashes requested by index.
1745
+ ...request.txIndices.getTrueIndices().map((i)=>blockTxHashes[i].toString()),
1746
+ // explicitly requested tx hashes.
1747
+ ...request.txHashes.map((h)=>h.toString())
1748
+ ]);
1749
+ if (!returnedHashes.every((h)=>uniqueRequestedHashes.has(h))) {
1750
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1751
+ throw new ValidationError('Returned txs should be a subset of (tx hashes by index) U (explicitly requested tx hashes)');
1752
+ }
1753
+ // Tx indices information is optional, but if present, we need to verify a few things.
1754
+ if (!response.peerHasBlock()) {
1755
+ this.logger.debug(`Peer ${peerId.toString()} signalled missing block`);
1756
+ return true;
1757
+ }
1758
+ // If the response has a non-empty bitvector, it needs to be consistent with the number of txs in the block.
1759
+ if (response.txIndices.getLength() !== blockTxHashes.length) {
1624
1760
  this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1625
- throw new ValidationError(`Received more txs (${response.txs.length}) than requested-and-available (${maxReturnable})`);
1761
+ throw new ValidationError(`Received block txs with mismatched bitvector length: expected ${blockTxHashes.length}, got ${response.txIndices.getLength()}`);
1626
1762
  }
1627
- // Given proposal (should have locally), ensure returned txs are valid subset and match request indices
1628
- const proposal = await this.mempools.attestationPool.getBlockProposal(request.archiveRoot.toString());
1629
- if (proposal) {
1630
- // Build intersected indices
1631
- const intersectIdx = request.txIndices.getTrueIndices().filter((i)=>response.txIndices.isSet(i));
1632
- // Enforce subset membership and preserve increasing order by index.
1633
- const hashToIndexInProposal = new Map(proposal.txHashes.map((h, i)=>[
1634
- h.toString(),
1635
- i
1636
- ]));
1637
- const allowedIndexSet = new Set(intersectIdx);
1638
- const indices = returnedHashes.map((h)=>hashToIndexInProposal.get(h));
1639
- const allAllowed = indices.every((idx)=>idx !== undefined && allowedIndexSet.has(idx));
1640
- const strictlyIncreasing = indices.every((idx, i)=>i === 0 ? idx !== undefined : idx > indices[i - 1]);
1641
- if (!allAllowed || !strictlyIncreasing) {
1642
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1643
- throw new ValidationError('Returned txs do not match expected subset/order for requested indices');
1644
- }
1645
- } else {
1646
- // No local proposal, cannot check the membership/order of the returned txs
1647
- this.logger.warn(`Block proposal not found for archive root ${request.archiveRoot.toString()}; cannot validate membership/order of returned txs`);
1648
- return false;
1763
+ // For every index we requested that the peer claims to have (per its bitvector), the
1764
+ // corresponding tx must be present in the response. A peer advertising availability but
1765
+ // withholding the tx is inconsistent.
1766
+ const returnedHashSet = new Set(returnedHashes);
1767
+ const expectedIndices = request.txIndices.getTrueIndices().filter((i)=>response.txIndices.isSet(i));
1768
+ const missingExpected = expectedIndices.some((i)=>!returnedHashSet.has(blockTxHashes[i].toString()));
1769
+ if (missingExpected) {
1770
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1771
+ throw new ValidationError('Peer advertised requested txs via indices but did not return them');
1649
1772
  }
1650
- await Promise.all(response.txs.map((tx)=>this.validateRequestedTx(tx, peerId, requestedTxValidator)));
1651
1773
  return true;
1652
1774
  } catch (e) {
1653
1775
  if (e instanceof ValidationError) {
@@ -1658,104 +1780,8 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
1658
1780
  return false;
1659
1781
  }
1660
1782
  }
1661
- /**
1662
- * Validate a collection of txs that has been requested from a peer.
1663
- *
1664
- * The core component of this validator is that each tx hash MUST match the requested tx hash,
1665
- * In order to perform this check, the tx proof must be verified.
1666
- *
1667
- * Note: This function is called from within `ReqResp.sendRequest` as part of the
1668
- * ReqRespSubProtocol.TX subprotocol validation.
1669
- *
1670
- * @param requestedTxHash - The collection of the txs that was requested.
1671
- * @param responseTx - The collection of txs that was received as a response to the request.
1672
- * @param peerId - The peer ID of the peer that sent the tx.
1673
- * @returns True if the whole collection of txs is valid, false otherwise.
1674
- */ async validateRequestedTxs(requestedTxHash, responseTx, peerId) {
1675
- const requested = new Set(requestedTxHash.map((h)=>h.toString()));
1676
- const requestedTxValidator = this.createRequestedTxValidator();
1677
- //TODO: (mralj) - this is somewhat naive implementation, if single tx is invalid we consider the whole response invalid.
1678
- // I think we should still extract the valid txs and return them, so that we can still use the response.
1679
- try {
1680
- await Promise.all(responseTx.map((tx)=>this.validateRequestedTx(tx, peerId, requestedTxValidator, requested)));
1681
- return true;
1682
- } catch (e) {
1683
- if (e instanceof ValidationError) {
1684
- this.logger.warn(`Failed to validate requested txs from peer ${peerId.toString()}, reason ${e.message}`);
1685
- } else {
1686
- this.logger.error(`Error during validation of requested txs`, e);
1687
- }
1688
- return false;
1689
- }
1690
- }
1691
- /**
1692
- * Validates a BLOCK response.
1693
- *
1694
- * If a local copy exists, enforces hash equality. If missing, rejects (no penalty) since the hash cannot be verified.
1695
- * Penalizes on block number mismatch or hash mismatch.
1696
- *
1697
- * @param requestedBlockNumber - The requested block number.
1698
- * @param responseBlock - The block returned by the peer.
1699
- * @param peerId - The peer that returned the block.
1700
- * @returns True if the response is valid, false otherwise.
1701
- */ async validateRequestedBlock(requestedBlockNumber, responseBlock, peerId) {
1702
- try {
1703
- const reqNum = Number(requestedBlockNumber.toString());
1704
- if (responseBlock.number !== reqNum) {
1705
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1706
- return false;
1707
- }
1708
- const local = await this.archiver.getBlock(BlockNumber(reqNum));
1709
- if (!local) {
1710
- // We are missing the local block; we cannot verify the hash yet. Reject without penalizing.
1711
- // TODO: Consider extending this validator to accept an expected hash or
1712
- // performing quorum-based checks when using P2P syncing prior to L1 sync.
1713
- this.logger.warn(`Local block ${reqNum} not found; rejecting BLOCK response without hash verification`);
1714
- return false;
1715
- }
1716
- const [localHash, respHash] = await Promise.all([
1717
- local.hash(),
1718
- responseBlock.hash()
1719
- ]);
1720
- if (!localHash.equals(respHash)) {
1721
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1722
- return false;
1723
- }
1724
- return true;
1725
- } catch (e) {
1726
- this.logger.warn(`Error validating requested block`, e);
1727
- return false;
1728
- }
1729
- }
1730
- async validateRequestedTx(tx, peerId, txValidator, requested) {
1731
- const penalize = (severity)=>this.peerManager.penalizePeer(peerId, severity);
1732
- if (requested && !requested.has(tx.getTxHash().toString())) {
1733
- penalize(PeerErrorSeverity.MidToleranceError);
1734
- throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that was not requested.`);
1735
- }
1736
- const { result } = await txValidator.validateTx(tx);
1737
- if (result === 'invalid') {
1738
- penalize(PeerErrorSeverity.LowToleranceError);
1739
- throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that is invalid.`);
1740
- }
1741
- }
1742
- createRequestedTxValidator() {
1743
- return createTxValidatorForReqResponseReceivedTxs(this.proofVerifier, {
1744
- l1ChainId: this.config.l1ChainId,
1745
- rollupVersion: this.config.rollupVersion
1746
- });
1747
- }
1748
- async getGasFees(blockNumber) {
1749
- if (blockNumber === this.feesCache?.blockNumber) {
1750
- return this.feesCache.gasFees;
1751
- }
1752
- const header = await this.archiver.getBlockHeader(blockNumber);
1753
- const gasFees = header?.globalVariables.gasFees ?? GasFees.empty();
1754
- this.feesCache = {
1755
- blockNumber,
1756
- gasFees
1757
- };
1758
- return gasFees;
1783
+ getGasFees() {
1784
+ return this.blockMinFeesProvider.getCurrentMinFees();
1759
1785
  }
1760
1786
  /**
1761
1787
  * Get the BatchTxRequesterLibP2PService dependencies for creating BatchTxRequester instances
@@ -1766,16 +1792,18 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
1766
1792
  txValidatorConfig: {
1767
1793
  l1ChainId: this.config.l1ChainId,
1768
1794
  rollupVersion: this.config.rollupVersion,
1769
- proofVerifier: this.proofVerifier
1795
+ proofVerifier: this.proofVerifier,
1796
+ txValidationCache: this.txValidationCache
1770
1797
  },
1771
- peerScoring: this.peerManager
1798
+ peerScoring: this.peerManager,
1799
+ validateRequestedBlockTxsConsistency: this.validateRequestedBlockTxsConsistency.bind(this)
1772
1800
  };
1773
1801
  }
1774
1802
  async validateTxsReceivedInBlockProposal(txs) {
1775
1803
  const validator = createTxValidatorForBlockProposalReceivedTxs(this.proofVerifier, {
1776
1804
  l1ChainId: this.config.l1ChainId,
1777
1805
  rollupVersion: this.config.rollupVersion
1778
- }, this.logger.getBindings());
1806
+ }, this.logger.getBindings(), this.txValidationCache);
1779
1807
  const results = await Promise.all(txs.map(async (tx)=>{
1780
1808
  const result = await validator.validateTx(tx);
1781
1809
  return result.result !== 'invalid';
@@ -1785,17 +1813,17 @@ _dec = trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId
1785
1813
  }
1786
1814
  }
1787
1815
  /** Creates the first stage (fast) validators for gossiped transactions. */ async createFirstStageMessageValidators(currentBlockNumber, nextSlotTimestamp) {
1788
- const gasFees = await this.getGasFees(currentBlockNumber);
1816
+ const gasFees = await this.getGasFees();
1789
1817
  const allowedInSetup = [
1790
1818
  ...await getDefaultAllowedSetupFunctions(),
1791
1819
  ...this.config.txPublicSetupAllowListExtend ?? []
1792
1820
  ];
1793
1821
  const blockNumber = BlockNumber(currentBlockNumber + 1);
1794
1822
  const l1Constants = await this.archiver.getL1Constants();
1823
+ const networkTxGasLimits = getNetworkTxGasLimits(this.config, l1Constants);
1795
1824
  return createFirstStageTxValidationsForGossipedTransactions(nextSlotTimestamp, blockNumber, this.worldStateSynchronizer, gasFees, this.config.l1ChainId, this.config.rollupVersion, protocolContractsHash, this.archiver, !this.config.disableTransactions, allowedInSetup, this.logger.getBindings(), {
1796
- rollupManaLimit: l1Constants.rollupManaLimit,
1797
- maxBlockL2Gas: this.config.validateMaxL2BlockGas,
1798
- maxBlockDAGas: this.config.validateMaxDABlockGas
1825
+ maxTxL2Gas: networkTxGasLimits.l2Gas,
1826
+ maxTxDAGas: networkTxGasLimits.daGas
1799
1827
  });
1800
1828
  }
1801
1829
  /** Creates the second stage (expensive proof verification) validators for gossiped transactions. */ createSecondStageMessageValidators() {