@aztec/p2p 0.0.1-commit.b655e406 → 0.0.1-commit.b9865e97

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 (605) hide show
  1. package/README.md +129 -3
  2. package/dest/bootstrap/bootstrap.d.ts +4 -3
  3. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  4. package/dest/bootstrap/bootstrap.js +13 -5
  5. package/dest/client/factory.d.ts +13 -12
  6. package/dest/client/factory.d.ts.map +1 -1
  7. package/dest/client/factory.js +65 -21
  8. package/dest/client/index.d.ts +1 -1
  9. package/dest/client/interface.d.ts +72 -35
  10. package/dest/client/interface.d.ts.map +1 -1
  11. package/dest/client/p2p_client.d.ts +59 -86
  12. package/dest/client/p2p_client.d.ts.map +1 -1
  13. package/dest/client/p2p_client.js +677 -339
  14. package/dest/config.d.ts +169 -87
  15. package/dest/config.d.ts.map +1 -1
  16. package/dest/config.js +176 -56
  17. package/dest/enr/generate-enr.d.ts +1 -1
  18. package/dest/enr/index.d.ts +1 -1
  19. package/dest/errors/attestation-pool.error.d.ts +7 -0
  20. package/dest/errors/attestation-pool.error.d.ts.map +1 -0
  21. package/dest/errors/attestation-pool.error.js +12 -0
  22. package/dest/errors/p2p-service.error.d.ts +9 -0
  23. package/dest/errors/p2p-service.error.d.ts.map +1 -0
  24. package/dest/errors/p2p-service.error.js +10 -0
  25. package/dest/errors/reqresp.error.d.ts +1 -20
  26. package/dest/errors/reqresp.error.d.ts.map +1 -1
  27. package/dest/errors/reqresp.error.js +0 -21
  28. package/dest/errors/tx-pool.error.d.ts +8 -0
  29. package/dest/errors/tx-pool.error.d.ts.map +1 -0
  30. package/dest/errors/tx-pool.error.js +9 -0
  31. package/dest/index.d.ts +2 -2
  32. package/dest/index.d.ts.map +1 -1
  33. package/dest/index.js +1 -1
  34. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +154 -57
  35. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  36. package/dest/mem_pools/attestation_pool/attestation_pool.js +511 -3
  37. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +2 -2
  38. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  39. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +651 -294
  40. package/dest/mem_pools/attestation_pool/index.d.ts +2 -3
  41. package/dest/mem_pools/attestation_pool/index.d.ts.map +1 -1
  42. package/dest/mem_pools/attestation_pool/index.js +1 -2
  43. package/dest/mem_pools/attestation_pool/mocks.d.ts +234 -10
  44. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  45. package/dest/mem_pools/attestation_pool/mocks.js +19 -13
  46. package/dest/mem_pools/index.d.ts +3 -3
  47. package/dest/mem_pools/index.d.ts.map +1 -1
  48. package/dest/mem_pools/index.js +1 -1
  49. package/dest/mem_pools/instrumentation.d.ts +11 -1
  50. package/dest/mem_pools/instrumentation.d.ts.map +1 -1
  51. package/dest/mem_pools/instrumentation.js +55 -10
  52. package/dest/mem_pools/interface.d.ts +6 -7
  53. package/dest/mem_pools/interface.d.ts.map +1 -1
  54. package/dest/mem_pools/tx_pool_v2/archive/index.d.ts +2 -0
  55. package/dest/mem_pools/tx_pool_v2/archive/index.d.ts.map +1 -0
  56. package/dest/mem_pools/tx_pool_v2/archive/index.js +1 -0
  57. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts +39 -0
  58. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts.map +1 -0
  59. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.js +97 -0
  60. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +104 -0
  61. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -0
  62. package/dest/mem_pools/tx_pool_v2/deleted_pool.js +251 -0
  63. package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts +47 -0
  64. package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts.map +1 -0
  65. package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.js +128 -0
  66. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts +17 -0
  67. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -0
  68. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +94 -0
  69. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts +19 -0
  70. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts.map +1 -0
  71. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.js +97 -0
  72. package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts +11 -0
  73. package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts.map +1 -0
  74. package/dest/mem_pools/tx_pool_v2/eviction/index.js +12 -0
  75. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts +16 -0
  76. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts.map +1 -0
  77. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.js +62 -0
  78. package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts +180 -0
  79. package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts.map +1 -0
  80. package/dest/mem_pools/tx_pool_v2/eviction/interfaces.js +25 -0
  81. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts +15 -0
  82. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts.map +1 -0
  83. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.js +65 -0
  84. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts +17 -0
  85. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts.map +1 -0
  86. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +93 -0
  87. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts +16 -0
  88. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts.map +1 -0
  89. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.js +78 -0
  90. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts +20 -0
  91. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts.map +1 -0
  92. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.js +75 -0
  93. package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts +15 -0
  94. package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts.map +1 -0
  95. package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.js +19 -0
  96. package/dest/mem_pools/tx_pool_v2/index.d.ts +6 -0
  97. package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -0
  98. package/dest/mem_pools/tx_pool_v2/index.js +5 -0
  99. package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts +15 -0
  100. package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts.map +1 -0
  101. package/dest/mem_pools/tx_pool_v2/instrumentation.js +43 -0
  102. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +218 -0
  103. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -0
  104. package/dest/mem_pools/tx_pool_v2/interfaces.js +10 -0
  105. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +137 -0
  106. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -0
  107. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +223 -0
  108. package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.d.ts +26 -0
  109. package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.d.ts.map +1 -0
  110. package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.js +70 -0
  111. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +108 -0
  112. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -0
  113. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +337 -0
  114. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +62 -0
  115. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -0
  116. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +167 -0
  117. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +78 -0
  118. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -0
  119. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +914 -0
  120. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +14 -7
  121. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
  122. package/dest/msg_validators/attestation_validator/attestation_validator.js +84 -26
  123. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +24 -0
  124. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -0
  125. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +75 -0
  126. package/dest/msg_validators/attestation_validator/index.d.ts +2 -1
  127. package/dest/msg_validators/attestation_validator/index.d.ts.map +1 -1
  128. package/dest/msg_validators/attestation_validator/index.js +1 -0
  129. package/dest/msg_validators/clock_tolerance.d.ts +32 -0
  130. package/dest/msg_validators/clock_tolerance.d.ts.map +1 -0
  131. package/dest/msg_validators/clock_tolerance.js +90 -0
  132. package/dest/msg_validators/index.d.ts +2 -2
  133. package/dest/msg_validators/index.d.ts.map +1 -1
  134. package/dest/msg_validators/index.js +1 -1
  135. package/dest/msg_validators/msg_seen_validator/msg_seen_validator.d.ts +1 -1
  136. package/dest/msg_validators/msg_seen_validator/msg_seen_validator.d.ts.map +1 -1
  137. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +15 -0
  138. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -0
  139. package/dest/msg_validators/proposal_validator/block_proposal_validator.js +14 -0
  140. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +15 -0
  141. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -0
  142. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +20 -0
  143. package/dest/msg_validators/proposal_validator/index.d.ts +4 -0
  144. package/dest/msg_validators/proposal_validator/index.d.ts.map +1 -0
  145. package/dest/msg_validators/proposal_validator/index.js +3 -0
  146. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +26 -0
  147. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -0
  148. package/dest/msg_validators/proposal_validator/proposal_validator.js +150 -0
  149. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +4 -4
  150. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
  151. package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +11 -18
  152. package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts +2 -1
  153. package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts.map +1 -1
  154. package/dest/msg_validators/tx_validator/allowed_public_setup.js +25 -21
  155. package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts +17 -0
  156. package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts.map +1 -0
  157. package/dest/msg_validators/tx_validator/allowed_setup_helpers.js +24 -0
  158. package/dest/msg_validators/tx_validator/archive_cache.d.ts +3 -3
  159. package/dest/msg_validators/tx_validator/archive_cache.d.ts.map +1 -1
  160. package/dest/msg_validators/tx_validator/block_header_validator.d.ts +20 -6
  161. package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
  162. package/dest/msg_validators/tx_validator/block_header_validator.js +4 -3
  163. package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts +15 -0
  164. package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts.map +1 -0
  165. package/dest/msg_validators/tx_validator/cached_tx_validator.js +19 -0
  166. package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts +9 -0
  167. package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts.map +1 -0
  168. package/dest/msg_validators/tx_validator/contract_instance_validator.js +48 -0
  169. package/dest/msg_validators/tx_validator/data_validator.d.ts +4 -1
  170. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
  171. package/dest/msg_validators/tx_validator/data_validator.js +40 -3
  172. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +15 -4
  173. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
  174. package/dest/msg_validators/tx_validator/double_spend_validator.js +7 -6
  175. package/dest/msg_validators/tx_validator/factory.d.ts +140 -6
  176. package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
  177. package/dest/msg_validators/tx_validator/factory.js +259 -58
  178. package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts +10 -0
  179. package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts.map +1 -0
  180. package/dest/msg_validators/tx_validator/fee_payer_balance.js +24 -0
  181. package/dest/msg_validators/tx_validator/gas_validator.d.ts +100 -3
  182. package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
  183. package/dest/msg_validators/tx_validator/gas_validator.js +146 -67
  184. package/dest/msg_validators/tx_validator/index.d.ts +6 -1
  185. package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
  186. package/dest/msg_validators/tx_validator/index.js +5 -0
  187. package/dest/msg_validators/tx_validator/metadata_validator.d.ts +4 -3
  188. package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
  189. package/dest/msg_validators/tx_validator/metadata_validator.js +6 -6
  190. package/dest/msg_validators/tx_validator/nullifier_cache.d.ts +14 -0
  191. package/dest/msg_validators/tx_validator/nullifier_cache.d.ts.map +1 -0
  192. package/dest/msg_validators/tx_validator/nullifier_cache.js +24 -0
  193. package/dest/msg_validators/tx_validator/phases_validator.d.ts +24 -3
  194. package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
  195. package/dest/msg_validators/tx_validator/phases_validator.js +78 -28
  196. package/dest/msg_validators/tx_validator/size_validator.d.ts +8 -0
  197. package/dest/msg_validators/tx_validator/size_validator.d.ts.map +1 -0
  198. package/dest/msg_validators/tx_validator/size_validator.js +23 -0
  199. package/dest/msg_validators/tx_validator/test_utils.d.ts +2 -2
  200. package/dest/msg_validators/tx_validator/test_utils.d.ts.map +1 -1
  201. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +23 -5
  202. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -1
  203. package/dest/msg_validators/tx_validator/timestamp_validator.js +8 -8
  204. package/dest/msg_validators/tx_validator/tx_permitted_validator.d.ts +3 -2
  205. package/dest/msg_validators/tx_validator/tx_permitted_validator.d.ts.map +1 -1
  206. package/dest/msg_validators/tx_validator/tx_permitted_validator.js +2 -2
  207. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +4 -2
  208. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -1
  209. package/dest/msg_validators/tx_validator/tx_proof_validator.js +4 -2
  210. package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts +48 -0
  211. package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts.map +1 -0
  212. package/dest/msg_validators/tx_validator/tx_validation_cache.js +68 -0
  213. package/dest/services/data_store.d.ts +1 -1
  214. package/dest/services/data_store.d.ts.map +1 -1
  215. package/dest/services/data_store.js +14 -10
  216. package/dest/services/discv5/discV5_service.d.ts +2 -1
  217. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  218. package/dest/services/discv5/discV5_service.js +36 -9
  219. package/dest/services/dummy_service.d.ts +36 -16
  220. package/dest/services/dummy_service.d.ts.map +1 -1
  221. package/dest/services/dummy_service.js +58 -14
  222. package/dest/services/encoding.d.ts +7 -3
  223. package/dest/services/encoding.d.ts.map +1 -1
  224. package/dest/services/encoding.js +23 -15
  225. package/dest/services/gossipsub/index.d.ts +3 -0
  226. package/dest/services/gossipsub/index.d.ts.map +1 -0
  227. package/dest/services/gossipsub/index.js +2 -0
  228. package/dest/services/gossipsub/scoring.d.ts +21 -3
  229. package/dest/services/gossipsub/scoring.d.ts.map +1 -1
  230. package/dest/services/gossipsub/scoring.js +24 -7
  231. package/dest/services/gossipsub/topic_score_params.d.ts +184 -0
  232. package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -0
  233. package/dest/services/gossipsub/topic_score_params.js +363 -0
  234. package/dest/services/index.d.ts +2 -1
  235. package/dest/services/index.d.ts.map +1 -1
  236. package/dest/services/index.js +1 -0
  237. package/dest/services/libp2p/instrumentation.d.ts +5 -1
  238. package/dest/services/libp2p/instrumentation.d.ts.map +1 -1
  239. package/dest/services/libp2p/instrumentation.js +49 -70
  240. package/dest/services/libp2p/libp2p_service.d.ts +118 -109
  241. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  242. package/dest/services/libp2p/libp2p_service.js +1286 -430
  243. package/dest/services/peer-manager/interface.d.ts +1 -1
  244. package/dest/services/peer-manager/metrics.d.ts +11 -2
  245. package/dest/services/peer-manager/metrics.d.ts.map +1 -1
  246. package/dest/services/peer-manager/metrics.js +45 -16
  247. package/dest/services/peer-manager/peer_manager.d.ts +7 -34
  248. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  249. package/dest/services/peer-manager/peer_manager.js +46 -23
  250. package/dest/services/peer-manager/peer_scoring.d.ts +12 -2
  251. package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
  252. package/dest/services/peer-manager/peer_scoring.js +99 -13
  253. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +44 -0
  254. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -0
  255. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +534 -0
  256. package/dest/services/reqresp/batch-tx-requester/config.d.ts +17 -0
  257. package/dest/services/reqresp/batch-tx-requester/config.d.ts.map +1 -0
  258. package/dest/services/reqresp/batch-tx-requester/config.js +27 -0
  259. package/dest/services/reqresp/batch-tx-requester/interface.d.ts +50 -0
  260. package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -0
  261. package/dest/services/reqresp/batch-tx-requester/interface.js +1 -0
  262. package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts +35 -0
  263. package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts.map +1 -0
  264. package/dest/services/reqresp/batch-tx-requester/missing_txs.js +136 -0
  265. package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts +62 -0
  266. package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts.map +1 -0
  267. package/dest/services/reqresp/batch-tx-requester/peer_collection.js +176 -0
  268. package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts +11 -0
  269. package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts.map +1 -0
  270. package/dest/services/reqresp/batch-tx-requester/tx_validator.js +7 -0
  271. package/dest/services/reqresp/config.d.ts +3 -3
  272. package/dest/services/reqresp/config.d.ts.map +1 -1
  273. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +2 -4
  274. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +1 -1
  275. package/dest/services/reqresp/connection-sampler/connection_sampler.js +12 -0
  276. package/dest/services/reqresp/constants.d.ts +12 -0
  277. package/dest/services/reqresp/constants.d.ts.map +1 -0
  278. package/dest/services/reqresp/constants.js +7 -0
  279. package/dest/services/reqresp/index.d.ts +1 -1
  280. package/dest/services/reqresp/interface.d.ts +27 -18
  281. package/dest/services/reqresp/interface.d.ts.map +1 -1
  282. package/dest/services/reqresp/interface.js +23 -19
  283. package/dest/services/reqresp/metrics.d.ts +6 -5
  284. package/dest/services/reqresp/metrics.d.ts.map +1 -1
  285. package/dest/services/reqresp/metrics.js +16 -21
  286. package/dest/services/reqresp/protocols/auth.d.ts +2 -2
  287. package/dest/services/reqresp/protocols/auth.d.ts.map +1 -1
  288. package/dest/services/reqresp/protocols/auth.js +2 -2
  289. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +8 -3
  290. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -1
  291. package/dest/services/reqresp/protocols/block_txs/bitvector.js +15 -2
  292. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +7 -5
  293. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
  294. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +35 -14
  295. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +37 -11
  296. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
  297. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +85 -21
  298. package/dest/services/reqresp/protocols/block_txs/index.d.ts +1 -1
  299. package/dest/services/reqresp/protocols/goodbye.d.ts +1 -1
  300. package/dest/services/reqresp/protocols/goodbye.d.ts.map +1 -1
  301. package/dest/services/reqresp/protocols/index.d.ts +1 -2
  302. package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
  303. package/dest/services/reqresp/protocols/index.js +0 -1
  304. package/dest/services/reqresp/protocols/ping.d.ts +1 -1
  305. package/dest/services/reqresp/protocols/status.d.ts +6 -5
  306. package/dest/services/reqresp/protocols/status.d.ts.map +1 -1
  307. package/dest/services/reqresp/protocols/status.js +7 -3
  308. package/dest/services/reqresp/protocols/tx.d.ts +8 -3
  309. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
  310. package/dest/services/reqresp/protocols/tx.js +21 -3
  311. package/dest/services/reqresp/rate-limiter/index.d.ts +1 -1
  312. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +6 -5
  313. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
  314. package/dest/services/reqresp/rate-limiter/rate_limiter.js +10 -8
  315. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
  316. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
  317. package/dest/services/reqresp/rate-limiter/rate_limits.js +0 -10
  318. package/dest/services/reqresp/reqresp.d.ts +12 -69
  319. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  320. package/dest/services/reqresp/reqresp.js +448 -199
  321. package/dest/services/reqresp/status.d.ts +2 -2
  322. package/dest/services/reqresp/status.d.ts.map +1 -1
  323. package/dest/services/service.d.ts +62 -13
  324. package/dest/services/service.d.ts.map +1 -1
  325. package/dest/services/tx_collection/config.d.ts +11 -11
  326. package/dest/services/tx_collection/config.d.ts.map +1 -1
  327. package/dest/services/tx_collection/config.js +28 -27
  328. package/dest/services/tx_collection/file_store_tx_collection.d.ts +37 -0
  329. package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -0
  330. package/dest/services/tx_collection/file_store_tx_collection.js +127 -0
  331. package/dest/services/tx_collection/file_store_tx_source.d.ts +38 -0
  332. package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -0
  333. package/dest/services/tx_collection/file_store_tx_source.js +100 -0
  334. package/dest/services/tx_collection/index.d.ts +3 -2
  335. package/dest/services/tx_collection/index.d.ts.map +1 -1
  336. package/dest/services/tx_collection/index.js +1 -0
  337. package/dest/services/tx_collection/instrumentation.d.ts +1 -1
  338. package/dest/services/tx_collection/instrumentation.d.ts.map +1 -1
  339. package/dest/services/tx_collection/instrumentation.js +9 -13
  340. package/dest/services/tx_collection/request_tracker.d.ts +53 -0
  341. package/dest/services/tx_collection/request_tracker.d.ts.map +1 -0
  342. package/dest/services/tx_collection/request_tracker.js +84 -0
  343. package/dest/services/tx_collection/tx_collection.d.ts +51 -57
  344. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
  345. package/dest/services/tx_collection/tx_collection.js +298 -70
  346. package/dest/services/tx_collection/tx_collection_sink.d.ts +20 -10
  347. package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
  348. package/dest/services/tx_collection/tx_collection_sink.js +28 -31
  349. package/dest/services/tx_collection/tx_source.d.ts +13 -7
  350. package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
  351. package/dest/services/tx_collection/tx_source.js +26 -7
  352. package/dest/services/tx_file_store/config.d.ts +16 -0
  353. package/dest/services/tx_file_store/config.d.ts.map +1 -0
  354. package/dest/services/tx_file_store/config.js +22 -0
  355. package/dest/services/tx_file_store/index.d.ts +4 -0
  356. package/dest/services/tx_file_store/index.d.ts.map +1 -0
  357. package/dest/services/tx_file_store/index.js +3 -0
  358. package/dest/services/tx_file_store/instrumentation.d.ts +15 -0
  359. package/dest/services/tx_file_store/instrumentation.d.ts.map +1 -0
  360. package/dest/services/tx_file_store/instrumentation.js +29 -0
  361. package/dest/services/tx_file_store/tx_file_store.d.ts +46 -0
  362. package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -0
  363. package/dest/services/tx_file_store/tx_file_store.js +142 -0
  364. package/dest/services/tx_provider.d.ts +9 -5
  365. package/dest/services/tx_provider.d.ts.map +1 -1
  366. package/dest/services/tx_provider.js +23 -10
  367. package/dest/services/tx_provider_instrumentation.d.ts +5 -2
  368. package/dest/services/tx_provider_instrumentation.d.ts.map +1 -1
  369. package/dest/services/tx_provider_instrumentation.js +14 -14
  370. package/dest/test-helpers/generate-peer-id-private-keys.d.ts +1 -1
  371. package/dest/test-helpers/get-ports.d.ts +1 -1
  372. package/dest/test-helpers/get-ports.d.ts.map +1 -1
  373. package/dest/test-helpers/index.d.ts +3 -1
  374. package/dest/test-helpers/index.d.ts.map +1 -1
  375. package/dest/test-helpers/index.js +2 -0
  376. package/dest/test-helpers/make-enrs.d.ts +1 -1
  377. package/dest/test-helpers/make-test-p2p-clients.d.ts +7 -8
  378. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  379. package/dest/test-helpers/make-test-p2p-clients.js +5 -3
  380. package/dest/test-helpers/mock-pubsub.d.ts +48 -8
  381. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  382. package/dest/test-helpers/mock-pubsub.js +115 -14
  383. package/dest/test-helpers/mock-tx-helpers.d.ts +2 -2
  384. package/dest/test-helpers/mock-tx-helpers.d.ts.map +1 -1
  385. package/dest/test-helpers/mock-tx-helpers.js +1 -1
  386. package/dest/test-helpers/reqresp-nodes.d.ts +5 -7
  387. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  388. package/dest/test-helpers/reqresp-nodes.js +19 -20
  389. package/dest/test-helpers/test_tx_provider.d.ts +42 -0
  390. package/dest/test-helpers/test_tx_provider.d.ts.map +1 -0
  391. package/dest/test-helpers/test_tx_provider.js +44 -0
  392. package/dest/test-helpers/testbench-utils.d.ts +161 -0
  393. package/dest/test-helpers/testbench-utils.d.ts.map +1 -0
  394. package/dest/test-helpers/testbench-utils.js +391 -0
  395. package/dest/testbench/p2p_client_testbench_worker.d.ts +26 -2
  396. package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -1
  397. package/dest/testbench/p2p_client_testbench_worker.js +275 -131
  398. package/dest/testbench/parse_log_file.d.ts +1 -1
  399. package/dest/testbench/testbench.d.ts +1 -1
  400. package/dest/testbench/worker_client_manager.d.ts +57 -6
  401. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  402. package/dest/testbench/worker_client_manager.js +277 -44
  403. package/dest/types/index.d.ts +1 -1
  404. package/dest/util.d.ts +14 -8
  405. package/dest/util.d.ts.map +1 -1
  406. package/dest/util.js +41 -11
  407. package/dest/versioning.d.ts +3 -6
  408. package/dest/versioning.d.ts.map +1 -1
  409. package/dest/versioning.js +3 -24
  410. package/package.json +20 -18
  411. package/src/bootstrap/bootstrap.ts +16 -5
  412. package/src/client/factory.ts +137 -46
  413. package/src/client/interface.ts +87 -43
  414. package/src/client/p2p_client.ts +352 -418
  415. package/src/client/test/p2p_client.batch_tx_requester.bench.README.md +197 -0
  416. package/src/config.ts +302 -65
  417. package/src/errors/attestation-pool.error.ts +13 -0
  418. package/src/errors/p2p-service.error.ts +11 -0
  419. package/src/errors/reqresp.error.ts +0 -25
  420. package/src/errors/tx-pool.error.ts +12 -0
  421. package/src/index.ts +1 -1
  422. package/src/mem_pools/attestation_pool/attestation_pool.ts +588 -56
  423. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +760 -319
  424. package/src/mem_pools/attestation_pool/index.ts +9 -2
  425. package/src/mem_pools/attestation_pool/mocks.ts +29 -17
  426. package/src/mem_pools/index.ts +2 -2
  427. package/src/mem_pools/instrumentation.ts +56 -10
  428. package/src/mem_pools/interface.ts +5 -7
  429. package/src/mem_pools/tx_pool_v2/README.md +283 -0
  430. package/src/mem_pools/tx_pool_v2/archive/index.ts +1 -0
  431. package/src/mem_pools/tx_pool_v2/archive/tx_archive.ts +112 -0
  432. package/src/mem_pools/tx_pool_v2/deleted_pool.ts +321 -0
  433. package/src/mem_pools/tx_pool_v2/eviction/eviction_manager.ts +160 -0
  434. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +122 -0
  435. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +125 -0
  436. package/src/mem_pools/tx_pool_v2/eviction/index.ts +28 -0
  437. package/src/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.ts +65 -0
  438. package/src/mem_pools/tx_pool_v2/eviction/interfaces.ts +219 -0
  439. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.ts +74 -0
  440. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +101 -0
  441. package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +91 -0
  442. package/src/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.ts +99 -0
  443. package/src/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.ts +32 -0
  444. package/src/mem_pools/tx_pool_v2/index.ts +12 -0
  445. package/src/mem_pools/tx_pool_v2/instrumentation.ts +69 -0
  446. package/src/mem_pools/tx_pool_v2/interfaces.ts +250 -0
  447. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +349 -0
  448. package/src/mem_pools/tx_pool_v2/tx_pool_bench_metrics.ts +77 -0
  449. package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +430 -0
  450. package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +238 -0
  451. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +1090 -0
  452. package/src/msg_validators/attestation_validator/README.md +49 -0
  453. package/src/msg_validators/attestation_validator/attestation_validator.ts +78 -33
  454. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +101 -0
  455. package/src/msg_validators/attestation_validator/index.ts +1 -0
  456. package/src/msg_validators/clock_tolerance.ts +121 -0
  457. package/src/msg_validators/index.ts +1 -1
  458. package/src/msg_validators/proposal_validator/README.md +123 -0
  459. package/src/msg_validators/proposal_validator/block_proposal_validator.ts +30 -0
  460. package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +41 -0
  461. package/src/msg_validators/proposal_validator/index.ts +3 -0
  462. package/src/msg_validators/proposal_validator/proposal_validator.ts +159 -0
  463. package/src/msg_validators/tx_validator/README.md +127 -0
  464. package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +8 -17
  465. package/src/msg_validators/tx_validator/allowed_public_setup.ts +22 -27
  466. package/src/msg_validators/tx_validator/allowed_setup_helpers.ts +31 -0
  467. package/src/msg_validators/tx_validator/archive_cache.ts +2 -2
  468. package/src/msg_validators/tx_validator/block_header_validator.ts +21 -8
  469. package/src/msg_validators/tx_validator/cached_tx_validator.ts +31 -0
  470. package/src/msg_validators/tx_validator/contract_instance_validator.ts +56 -0
  471. package/src/msg_validators/tx_validator/data_validator.ts +62 -7
  472. package/src/msg_validators/tx_validator/double_spend_validator.ts +15 -9
  473. package/src/msg_validators/tx_validator/factory.ts +427 -58
  474. package/src/msg_validators/tx_validator/fee_payer_balance.ts +44 -0
  475. package/src/msg_validators/tx_validator/gas_validator.ts +211 -77
  476. package/src/msg_validators/tx_validator/index.ts +5 -0
  477. package/src/msg_validators/tx_validator/metadata_validator.ts +31 -12
  478. package/src/msg_validators/tx_validator/nullifier_cache.ts +30 -0
  479. package/src/msg_validators/tx_validator/phases_validator.ts +90 -31
  480. package/src/msg_validators/tx_validator/size_validator.ts +22 -0
  481. package/src/msg_validators/tx_validator/test_utils.ts +1 -1
  482. package/src/msg_validators/tx_validator/timestamp_validator.ts +30 -19
  483. package/src/msg_validators/tx_validator/tx_permitted_validator.ts +8 -3
  484. package/src/msg_validators/tx_validator/tx_proof_validator.ts +10 -3
  485. package/src/msg_validators/tx_validator/tx_validation_cache.ts +101 -0
  486. package/src/services/data_store.ts +14 -19
  487. package/src/services/discv5/discV5_service.ts +39 -6
  488. package/src/services/dummy_service.ts +76 -38
  489. package/src/services/encoding.ts +22 -13
  490. package/src/services/gossipsub/README.md +641 -0
  491. package/src/services/gossipsub/index.ts +2 -0
  492. package/src/services/gossipsub/scoring.ts +29 -5
  493. package/src/services/gossipsub/topic_score_params.ts +519 -0
  494. package/src/services/index.ts +1 -0
  495. package/src/services/libp2p/instrumentation.ts +54 -72
  496. package/src/services/libp2p/libp2p_service.ts +1032 -480
  497. package/src/services/peer-manager/metrics.ts +51 -16
  498. package/src/services/peer-manager/peer_manager.ts +53 -15
  499. package/src/services/peer-manager/peer_scoring.ts +96 -7
  500. package/src/services/reqresp/README.md +215 -0
  501. package/src/services/reqresp/batch-tx-requester/README.md +344 -0
  502. package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +660 -0
  503. package/src/services/reqresp/batch-tx-requester/config.ts +40 -0
  504. package/src/services/reqresp/batch-tx-requester/interface.ts +61 -0
  505. package/src/services/reqresp/batch-tx-requester/missing_txs.ts +168 -0
  506. package/src/services/reqresp/batch-tx-requester/peer_collection.ts +249 -0
  507. package/src/services/reqresp/batch-tx-requester/tx_validator.ts +24 -0
  508. package/src/services/reqresp/config.ts +2 -2
  509. package/src/services/reqresp/connection-sampler/connection_sampler.ts +19 -1
  510. package/src/services/reqresp/constants.ts +14 -0
  511. package/src/services/reqresp/interface.ts +48 -46
  512. package/src/services/reqresp/metrics.ts +35 -27
  513. package/src/services/reqresp/protocols/auth.ts +2 -2
  514. package/src/services/reqresp/protocols/block_txs/bitvector.ts +19 -2
  515. package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +44 -17
  516. package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +109 -17
  517. package/src/services/reqresp/protocols/index.ts +0 -1
  518. package/src/services/reqresp/protocols/status.ts +16 -12
  519. package/src/services/reqresp/protocols/tx.ts +24 -5
  520. package/src/services/reqresp/rate-limiter/rate_limiter.ts +13 -9
  521. package/src/services/reqresp/rate-limiter/rate_limits.ts +0 -10
  522. package/src/services/reqresp/reqresp.ts +68 -224
  523. package/src/services/service.ts +83 -31
  524. package/src/services/tx_collection/config.ts +42 -37
  525. package/src/services/tx_collection/file_store_tx_collection.ts +153 -0
  526. package/src/services/tx_collection/file_store_tx_source.ts +129 -0
  527. package/src/services/tx_collection/index.ts +2 -1
  528. package/src/services/tx_collection/instrumentation.ts +5 -13
  529. package/src/services/tx_collection/request_tracker.ts +127 -0
  530. package/src/services/tx_collection/tx_collection.ts +368 -115
  531. package/src/services/tx_collection/tx_collection_sink.ts +32 -36
  532. package/src/services/tx_collection/tx_source.ts +28 -8
  533. package/src/services/tx_file_store/config.ts +37 -0
  534. package/src/services/tx_file_store/index.ts +3 -0
  535. package/src/services/tx_file_store/instrumentation.ts +36 -0
  536. package/src/services/tx_file_store/tx_file_store.ts +163 -0
  537. package/src/services/tx_provider.ts +34 -12
  538. package/src/services/tx_provider_instrumentation.ts +24 -14
  539. package/src/test-helpers/index.ts +2 -0
  540. package/src/test-helpers/make-test-p2p-clients.ts +7 -6
  541. package/src/test-helpers/mock-pubsub.ts +138 -15
  542. package/src/test-helpers/mock-tx-helpers.ts +1 -1
  543. package/src/test-helpers/reqresp-nodes.ts +18 -30
  544. package/src/test-helpers/test_tx_provider.ts +69 -0
  545. package/src/test-helpers/testbench-utils.ts +453 -0
  546. package/src/testbench/p2p_client_testbench_worker.ts +387 -128
  547. package/src/testbench/worker_client_manager.ts +355 -46
  548. package/src/util.ts +47 -16
  549. package/src/versioning.ts +3 -33
  550. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +0 -30
  551. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +0 -1
  552. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +0 -190
  553. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +0 -25
  554. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +0 -1
  555. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +0 -197
  556. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +0 -150
  557. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +0 -1
  558. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +0 -617
  559. package/dest/mem_pools/tx_pool/index.d.ts +0 -4
  560. package/dest/mem_pools/tx_pool/index.d.ts.map +0 -1
  561. package/dest/mem_pools/tx_pool/index.js +0 -3
  562. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +0 -80
  563. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +0 -1
  564. package/dest/mem_pools/tx_pool/memory_tx_pool.js +0 -238
  565. package/dest/mem_pools/tx_pool/priority.d.ts +0 -8
  566. package/dest/mem_pools/tx_pool/priority.d.ts.map +0 -1
  567. package/dest/mem_pools/tx_pool/priority.js +0 -10
  568. package/dest/mem_pools/tx_pool/tx_pool.d.ts +0 -122
  569. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +0 -1
  570. package/dest/mem_pools/tx_pool/tx_pool.js +0 -3
  571. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +0 -7
  572. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +0 -1
  573. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +0 -394
  574. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts +0 -12
  575. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts.map +0 -1
  576. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.js +0 -70
  577. package/dest/msg_validators/block_proposal_validator/index.d.ts +0 -2
  578. package/dest/msg_validators/block_proposal_validator/index.d.ts.map +0 -1
  579. package/dest/msg_validators/block_proposal_validator/index.js +0 -1
  580. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +0 -45
  581. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +0 -1
  582. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.js +0 -92
  583. package/dest/services/reqresp/protocols/block.d.ts +0 -9
  584. package/dest/services/reqresp/protocols/block.d.ts.map +0 -1
  585. package/dest/services/reqresp/protocols/block.js +0 -31
  586. package/dest/services/tx_collection/fast_tx_collection.d.ts +0 -56
  587. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +0 -1
  588. package/dest/services/tx_collection/fast_tx_collection.js +0 -300
  589. package/dest/services/tx_collection/slow_tx_collection.d.ts +0 -54
  590. package/dest/services/tx_collection/slow_tx_collection.d.ts.map +0 -1
  591. package/dest/services/tx_collection/slow_tx_collection.js +0 -176
  592. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +0 -256
  593. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +0 -253
  594. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +0 -750
  595. package/src/mem_pools/tx_pool/index.ts +0 -3
  596. package/src/mem_pools/tx_pool/memory_tx_pool.ts +0 -283
  597. package/src/mem_pools/tx_pool/priority.ts +0 -13
  598. package/src/mem_pools/tx_pool/tx_pool.ts +0 -135
  599. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +0 -313
  600. package/src/msg_validators/block_proposal_validator/block_proposal_validator.ts +0 -81
  601. package/src/msg_validators/block_proposal_validator/index.ts +0 -1
  602. package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +0 -100
  603. package/src/services/reqresp/protocols/block.ts +0 -36
  604. package/src/services/tx_collection/fast_tx_collection.ts +0 -340
  605. package/src/services/tx_collection/slow_tx_collection.ts +0 -232
@@ -1,34 +1,41 @@
1
1
  import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
- import { randomInt } from '@aztec/foundation/crypto';
3
- import { Fr } from '@aztec/foundation/fields';
2
+ import { BlockNumber, type SlotNumber } from '@aztec/foundation/branded-types';
3
+ import { maxBy, merge } from '@aztec/foundation/collection';
4
4
  import { type Logger, createLibp2pComponentLogger, createLogger } from '@aztec/foundation/log';
5
- import { SerialQueue } from '@aztec/foundation/queue';
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
- import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
10
8
  import { protocolContractsHash } from '@aztec/protocol-contracts';
11
9
  import type { EthAddress, L2BlockSource } from '@aztec/stdlib/block';
12
10
  import type { ContractDataSource } from '@aztec/stdlib/contract';
13
- import { GasFees } from '@aztec/stdlib/gas';
11
+ import { type BlockMinFeesProvider, GasFees } from '@aztec/stdlib/gas';
14
12
  import type { ClientProtocolCircuitVerifier, PeerInfo, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
15
13
  import {
16
- BlockAttestation,
17
14
  BlockProposal,
15
+ CheckpointAttestation,
16
+ CheckpointProposal,
17
+ type CheckpointProposalCore,
18
18
  type Gossipable,
19
- P2PClientType,
20
19
  P2PMessage,
21
20
  PeerErrorSeverity,
21
+ PeerErrorSeverityByHarshness,
22
22
  TopicType,
23
23
  createTopicString,
24
- getTopicsForClientAndConfig,
24
+ getTopicsForConfig,
25
25
  metricsTopicStrToLabels,
26
26
  } from '@aztec/stdlib/p2p';
27
27
  import { MerkleTreeId } from '@aztec/stdlib/trees';
28
- import { Tx, type TxHash, type TxValidationResult, type TxValidator } from '@aztec/stdlib/tx';
28
+ import { Tx, type TxValidationResult } from '@aztec/stdlib/tx';
29
29
  import type { UInt64 } from '@aztec/stdlib/types';
30
30
  import { compressComponentVersions } from '@aztec/stdlib/versioning';
31
- import { Attributes, OtelMetricsAdapter, type TelemetryClient, WithTracer, trackSpan } from '@aztec/telemetry-client';
31
+ import {
32
+ Attributes,
33
+ OtelMetricsAdapter,
34
+ SpanStatusCode,
35
+ type TelemetryClient,
36
+ WithTracer,
37
+ trackSpan,
38
+ } from '@aztec/telemetry-client';
32
39
 
33
40
  import {
34
41
  type GossipSub,
@@ -36,67 +43,78 @@ import {
36
43
  type GossipsubMessage,
37
44
  gossipsub,
38
45
  } from '@chainsafe/libp2p-gossipsub';
39
- import { createPeerScoreParams, createTopicScoreParams } from '@chainsafe/libp2p-gossipsub/score';
46
+ import { createPeerScoreParams } from '@chainsafe/libp2p-gossipsub/score';
40
47
  import { SignaturePolicy } from '@chainsafe/libp2p-gossipsub/types';
41
48
  import { noise } from '@chainsafe/libp2p-noise';
42
49
  import { yamux } from '@chainsafe/libp2p-yamux';
43
50
  import { bootstrap } from '@libp2p/bootstrap';
44
51
  import { identify } from '@libp2p/identify';
45
52
  import { type Message, type MultiaddrConnection, type PeerId, TopicValidatorResult } from '@libp2p/interface';
46
- import type { ConnectionManager } from '@libp2p/interface-internal';
53
+ import type { AddressManager, ConnectionManager } from '@libp2p/interface-internal';
47
54
  import { mplex } from '@libp2p/mplex';
48
55
  import { tcp } from '@libp2p/tcp';
56
+ import { multiaddr } from '@multiformats/multiaddr';
49
57
  import { ENR } from '@nethermindeth/enr';
50
58
  import { createLibp2p } from 'libp2p';
51
59
 
52
60
  import type { P2PConfig } from '../../config.js';
61
+ import { CheckpointProposalReceivedCallbackNotRegisteredError } from '../../errors/p2p-service.error.js';
53
62
  import type { MemPools } from '../../mem_pools/interface.js';
54
- import { AttestationValidator, BlockProposalValidator } from '../../msg_validators/index.js';
55
- import { MessageSeenValidator } from '../../msg_validators/msg_seen_validator/msg_seen_validator.js';
56
- import { getDefaultAllowedSetupFunctions } from '../../msg_validators/tx_validator/allowed_public_setup.js';
57
- import { type MessageValidator, createTxMessageValidators } from '../../msg_validators/tx_validator/factory.js';
58
63
  import {
59
- AggregateTxValidator,
60
- DataTxValidator,
64
+ BlockProposalValidator,
65
+ CheckpointAttestationValidator,
66
+ CheckpointProposalValidator,
61
67
  DoubleSpendTxValidator,
62
- MetadataTxValidator,
63
- TxProofValidator,
64
- } from '../../msg_validators/tx_validator/index.js';
68
+ FishermanAttestationValidator,
69
+ getDefaultAllowedSetupFunctions,
70
+ } from '../../msg_validators/index.js';
71
+ import { MessageSeenValidator } from '../../msg_validators/msg_seen_validator/msg_seen_validator.js';
72
+ import {
73
+ type TransactionValidator,
74
+ createFirstStageTxValidationsForGossipedTransactions,
75
+ createSecondStageTxValidationsForGossipedTransactions,
76
+ createTxValidatorForBlockProposalReceivedTxs,
77
+ } from '../../msg_validators/tx_validator/factory.js';
78
+ import { TxValidationCache } from '../../msg_validators/tx_validator/tx_validation_cache.js';
65
79
  import { GossipSubEvent } from '../../types/index.js';
66
80
  import { type PubSubLibp2p, convertToMultiaddr } from '../../util.js';
67
81
  import { getVersions } from '../../versioning.js';
68
82
  import { AztecDatastore } from '../data_store.js';
69
83
  import { DiscV5Service } from '../discv5/discV5_service.js';
70
84
  import { SnappyTransform, fastMsgIdFn, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
71
- import { gossipScoreThresholds } from '../gossipsub/scoring.js';
85
+ import { APP_SPECIFIC_WEIGHT, gossipScoreThresholds } from '../gossipsub/scoring.js';
86
+ import { createAllTopicScoreParams } from '../gossipsub/topic_score_params.js';
72
87
  import type { PeerManagerInterface } from '../peer-manager/interface.js';
73
88
  import { PeerManager } from '../peer-manager/peer_manager.js';
74
89
  import { PeerScoring } from '../peer-manager/peer_scoring.js';
90
+ import type { BatchTxRequesterLibP2PService } from '../reqresp/batch-tx-requester/interface.js';
75
91
  import type { P2PReqRespConfig } from '../reqresp/config.js';
76
92
  import {
77
- DEFAULT_SUB_PROTOCOL_VALIDATORS,
93
+ AuthRequest,
94
+ BlockTxsRequest,
95
+ BlockTxsResponse,
78
96
  type ReqRespInterface,
97
+ type ReqRespResponse,
79
98
  ReqRespSubProtocol,
80
99
  type ReqRespSubProtocolHandler,
81
100
  type ReqRespSubProtocolHandlers,
82
- type ReqRespSubProtocolValidators,
83
- type SubProtocolMap,
84
- ValidationError,
85
- } from '../reqresp/interface.js';
86
- import { reqRespBlockTxsHandler } from '../reqresp/protocols/block_txs/block_txs_handler.js';
87
- import { reqGoodbyeHandler } from '../reqresp/protocols/goodbye.js';
88
- import {
89
- AuthRequest,
90
- BlockTxsRequest,
91
- BlockTxsResponse,
92
101
  StatusMessage,
102
+ ValidationError,
93
103
  pingHandler,
94
- reqRespBlockHandler,
104
+ reqGoodbyeHandler,
105
+ reqRespBlockTxsHandler,
95
106
  reqRespStatusHandler,
96
107
  reqRespTxHandler,
97
- } from '../reqresp/protocols/index.js';
108
+ } from '../reqresp/index.js';
98
109
  import { ReqResp } from '../reqresp/reqresp.js';
99
- import type { P2PBlockReceivedCallback, P2PService, PeerDiscoveryService } from '../service.js';
110
+ import type {
111
+ P2PBlockReceivedCallback,
112
+ P2PCheckpointAttestationCallback,
113
+ P2PCheckpointReceivedCallback,
114
+ P2PDuplicateAttestationCallback,
115
+ P2PService,
116
+ PeerDiscoveryService,
117
+ } from '../service.js';
100
118
  import { P2PInstrumentation } from './instrumentation.js';
101
119
 
102
120
  interface ValidationResult {
@@ -108,26 +126,37 @@ interface ValidationResult {
108
126
  type ValidationOutcome = { allPassed: true } | { allPassed: false; failure: ValidationResult };
109
127
 
110
128
  // REFACTOR: Unify with the type above
111
- type ReceivedMessageValidationResult<T> =
112
- | { obj: T; result: Exclude<TopicValidatorResult, TopicValidatorResult.Reject> }
113
- | { obj?: undefined; result: TopicValidatorResult.Reject };
129
+ type ReceivedMessageValidationResult<T, M = undefined> =
130
+ | { obj: T; result: Exclude<TopicValidatorResult, TopicValidatorResult.Reject>; metadata?: M }
131
+ | { obj?: T; result: TopicValidatorResult.Reject; metadata?: M; severity: PeerErrorSeverity };
114
132
 
115
133
  /**
116
134
  * Lib P2P implementation of the P2PService interface.
117
135
  */
118
- export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends WithTracer implements P2PService {
119
- private jobQueue: SerialQueue = new SerialQueue();
136
+ export class LibP2PService extends WithTracer implements P2PService {
120
137
  private discoveryRunningPromise?: RunningPromise;
121
138
  private msgIdSeenValidators: Record<TopicType, MessageSeenValidator> = {} as Record<TopicType, MessageSeenValidator>;
122
139
 
123
140
  // Message validators
124
- private attestationValidator: AttestationValidator;
125
141
  private blockProposalValidator: BlockProposalValidator;
142
+ private checkpointProposalValidator: CheckpointProposalValidator;
143
+ private checkpointAttestationValidator: CheckpointAttestationValidator;
126
144
 
127
145
  private protocolVersion = '';
128
146
  private topicStrings: Record<TopicType, string> = {} as Record<TopicType, string>;
129
147
 
130
- private feesCache: { blockNumber: number; gasFees: GasFees } | undefined;
148
+ /** Callback invoked when a duplicate proposal is detected (triggers slashing). */
149
+ private duplicateProposalCallback?: (info: {
150
+ slot: SlotNumber;
151
+ proposer: EthAddress;
152
+ type: 'checkpoint' | 'block';
153
+ }) => void;
154
+
155
+ /** Callback invoked when a duplicate attestation is detected (triggers slashing). */
156
+ private duplicateAttestationCallback?: P2PDuplicateAttestationCallback;
157
+
158
+ /** Callback invoked when a valid checkpoint attestation is accepted into the pool. */
159
+ private checkpointAttestationCallback?: P2PCheckpointAttestationCallback;
131
160
 
132
161
  /**
133
162
  * Callback for when a block is received from a peer.
@@ -136,32 +165,58 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
136
165
  */
137
166
  private blockReceivedCallback: P2PBlockReceivedCallback;
138
167
 
168
+ /**
169
+ * Callback for when a checkpoint proposal is received from a peer.
170
+ * @param checkpoint - The checkpoint proposal received from the peer.
171
+ * @returns The attestations for the checkpoint, if any.
172
+ */
173
+ private allNodesCheckpointReceivedCallback: P2PCheckpointReceivedCallback;
174
+ /**
175
+ * Callback for when a checkpoint proposal is received - specifically for validators - from a peer.
176
+ * @param checkpoint - The checkpoint proposal received from the peer.
177
+ * @returns The attestations for the checkpoint, if any.
178
+ */
179
+ private validatorCheckpointReceivedCallback: P2PCheckpointReceivedCallback;
180
+
139
181
  private gossipSubEventHandler: (e: CustomEvent<GossipsubMessage>) => void;
140
182
 
183
+ private ipChangedHandler?: (ip: string) => void;
184
+ private discoveredP2pIp?: string;
185
+
141
186
  private instrumentation: P2PInstrumentation;
142
187
 
188
+ private telemetry: TelemetryClient;
189
+
190
+ protected logger: Logger;
191
+
143
192
  constructor(
144
- private clientType: T,
145
193
  private config: P2PConfig,
146
194
  protected node: PubSubLibp2p,
147
195
  private peerDiscoveryService: PeerDiscoveryService,
148
196
  private reqresp: ReqRespInterface,
149
- private peerManager: PeerManagerInterface,
150
- protected mempools: MemPools<T>,
151
- private archiver: L2BlockSource & ContractDataSource,
197
+ protected peerManager: PeerManagerInterface,
198
+ protected mempools: MemPools,
199
+ protected archiver: L2BlockSource & ContractDataSource,
152
200
  private epochCache: EpochCacheInterface,
153
201
  private proofVerifier: ClientProtocolCircuitVerifier,
154
202
  private worldStateSynchronizer: WorldStateSynchronizer,
203
+ private blockMinFeesProvider: BlockMinFeesProvider,
155
204
  telemetry: TelemetryClient,
156
- protected logger = createLogger('p2p:libp2p_service'),
205
+ logger: Logger = createLogger('p2p:libp2p_service'),
206
+ private txValidationCache?: TxValidationCache,
157
207
  ) {
158
208
  super(telemetry, 'LibP2PService');
209
+ this.telemetry = telemetry;
210
+
211
+ // Create child logger with fisherman prefix if in fisherman mode
212
+ this.logger = config.fishermanMode ? logger.createChild('[FISHERMAN]') : logger;
159
213
 
160
214
  this.instrumentation = new P2PInstrumentation(telemetry, 'LibP2PService');
161
215
 
162
216
  this.msgIdSeenValidators[TopicType.tx] = new MessageSeenValidator(config.seenMessageCacheSize);
163
217
  this.msgIdSeenValidators[TopicType.block_proposal] = new MessageSeenValidator(config.seenMessageCacheSize);
164
- this.msgIdSeenValidators[TopicType.block_attestation] = new MessageSeenValidator(config.seenMessageCacheSize);
218
+ this.msgIdSeenValidators[TopicType.checkpoint_proposal] = new MessageSeenValidator(config.seenMessageCacheSize);
219
+ this.msgIdSeenValidators[TopicType.checkpoint_attestation] = new MessageSeenValidator(config.seenMessageCacheSize);
165
220
 
166
221
  const versions = getVersions(config);
167
222
  this.protocolVersion = compressComponentVersions(versions);
@@ -169,27 +224,64 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
169
224
 
170
225
  this.topicStrings[TopicType.tx] = createTopicString(TopicType.tx, this.protocolVersion);
171
226
  this.topicStrings[TopicType.block_proposal] = createTopicString(TopicType.block_proposal, this.protocolVersion);
172
- this.topicStrings[TopicType.block_attestation] = createTopicString(
173
- TopicType.block_attestation,
227
+ this.topicStrings[TopicType.checkpoint_proposal] = createTopicString(
228
+ TopicType.checkpoint_proposal,
229
+ this.protocolVersion,
230
+ );
231
+ this.topicStrings[TopicType.checkpoint_attestation] = createTopicString(
232
+ TopicType.checkpoint_attestation,
174
233
  this.protocolVersion,
175
234
  );
176
235
 
177
- this.attestationValidator = new AttestationValidator(epochCache);
178
- this.blockProposalValidator = new BlockProposalValidator(epochCache, { txsPermitted: !config.disableTransactions });
236
+ const p2pPropagationTime = config.attestationPropagationTime;
237
+ const proposalValidatorOpts = {
238
+ txsPermitted: !config.disableTransactions,
239
+ maxTxsPerBlock: config.validateMaxTxsPerBlock ?? config.validateMaxTxsPerCheckpoint,
240
+ maxBlocksPerCheckpoint: config.maxBlocksPerCheckpoint,
241
+ p2pPropagationTime,
242
+ skipSlotValidation: config.skipProposalSlotValidation,
243
+ signatureContext: {
244
+ chainId: config.l1ChainId,
245
+ rollupAddress: config.rollupAddress,
246
+ },
247
+ };
248
+ this.blockProposalValidator = new BlockProposalValidator(epochCache, proposalValidatorOpts);
249
+ this.checkpointProposalValidator = new CheckpointProposalValidator(epochCache, proposalValidatorOpts);
250
+ const attestationValidatorOpts = {
251
+ l1PublishingTime: config.l1PublishingTime,
252
+ p2pPropagationTime,
253
+ signatureContext: proposalValidatorOpts.signatureContext,
254
+ };
255
+ this.checkpointAttestationValidator = config.fishermanMode
256
+ ? new FishermanAttestationValidator(epochCache, mempools.attestationPool, telemetry, attestationValidatorOpts)
257
+ : new CheckpointAttestationValidator(epochCache, attestationValidatorOpts);
179
258
 
180
259
  this.gossipSubEventHandler = this.handleGossipSubEvent.bind(this);
181
260
 
182
- this.blockReceivedCallback = async (block: BlockProposal): Promise<BlockAttestation[] | undefined> => {
183
- this.logger.debug(
184
- `Handler not yet registered: Block received callback not set. Received block for slot ${block.slotNumber.toNumber()} from peer.`,
185
- { p2pMessageIdentifier: await block.p2pMessageIdentifier() },
261
+ this.blockReceivedCallback = async (block: BlockProposal): Promise<boolean> => {
262
+ this.logger.warn(
263
+ `Handler for block received not yet registered on P2P service. Received block ${block.blockNumber} for slot ${block.slotNumber} from peer.`,
264
+ { p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier() },
186
265
  );
187
- return undefined;
266
+ return true;
267
+ };
268
+
269
+ this.allNodesCheckpointReceivedCallback = (
270
+ _checkpoint: CheckpointProposalCore,
271
+ ): Promise<CheckpointAttestation[] | undefined> => {
272
+ throw new CheckpointProposalReceivedCallbackNotRegisteredError();
273
+ };
274
+
275
+ this.validatorCheckpointReceivedCallback = (
276
+ _checkpoint: CheckpointProposalCore,
277
+ ): Promise<CheckpointAttestation[] | undefined> => {
278
+ return Promise.resolve(undefined);
188
279
  };
189
280
  }
190
281
 
191
- public updateConfig(config: Partial<P2PReqRespConfig>) {
282
+ public updateConfig(config: Partial<P2PReqRespConfig & Pick<P2PConfig, 'skipIncomingProposals'>>) {
192
283
  this.reqresp.updateConfig(config);
284
+ this.config = merge(this.config, config);
193
285
  }
194
286
 
195
287
  /**
@@ -198,20 +290,21 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
198
290
  * @param txPool - The transaction pool to be accessed by the service.
199
291
  * @returns The new service.
200
292
  */
201
- public static async new<T extends P2PClientType>(
202
- clientType: T,
293
+ public static async new(
203
294
  config: P2PConfig,
204
295
  peerId: PeerId,
205
296
  deps: {
206
- mempools: MemPools<T>;
297
+ mempools: MemPools;
207
298
  l2BlockSource: L2BlockSource & ContractDataSource;
208
299
  epochCache: EpochCacheInterface;
209
300
  proofVerifier: ClientProtocolCircuitVerifier;
210
301
  worldStateSynchronizer: WorldStateSynchronizer;
211
302
  peerStore: AztecAsyncKVStore;
303
+ blockMinFeesProvider: BlockMinFeesProvider;
212
304
  telemetry: TelemetryClient;
213
305
  logger: Logger;
214
306
  packageVersion: string;
307
+ txValidationCache?: TxValidationCache;
215
308
  },
216
309
  ) {
217
310
  const {
@@ -221,23 +314,25 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
221
314
  mempools,
222
315
  proofVerifier,
223
316
  peerStore,
317
+ blockMinFeesProvider,
224
318
  telemetry,
225
319
  logger,
226
320
  packageVersion,
321
+ txValidationCache,
227
322
  } = deps;
228
323
  const { p2pPort, maxPeerCount, listenAddress } = config;
229
324
  const bindAddrTcp = convertToMultiaddr(listenAddress, p2pPort, 'tcp');
230
325
 
231
326
  const datastore = new AztecDatastore(peerStore);
232
327
 
233
- const otelMetricsAdapter = new OtelMetricsAdapter(telemetry);
328
+ const otelMetricsAdapter = new OtelMetricsAdapter(telemetry, logger.getBindings());
234
329
 
235
330
  const peerDiscoveryService = new DiscV5Service(
236
331
  peerId,
237
332
  config,
238
333
  packageVersion,
239
334
  telemetry,
240
- createLogger(`${logger.module}:discv5_service`),
335
+ createLogger(`${logger.module}:discv5_service`, logger.getBindings()),
241
336
  );
242
337
 
243
338
  // Seed libp2p's bootstrap discovery with private and trusted peers
@@ -251,10 +346,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
251
346
  const versions = getVersions(config);
252
347
  const protocolVersion = compressComponentVersions(versions);
253
348
 
254
- const txTopic = createTopicString(TopicType.tx, protocolVersion);
255
- const blockProposalTopic = createTopicString(TopicType.block_proposal, protocolVersion);
256
- const blockAttestationTopic = createTopicString(TopicType.block_attestation, protocolVersion);
257
-
258
349
  const preferredPeersEnrs: ENR[] = config.preferredPeers.map(enr => ENR.decodeTxt(enr));
259
350
  const directPeers = (
260
351
  await Promise.all(
@@ -274,6 +365,19 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
274
365
 
275
366
  const announceTcpMultiaddr = config.p2pIp ? [convertToMultiaddr(config.p2pIp, p2pPort, 'tcp')] : [];
276
367
 
368
+ // Create dynamic topic score params based on network configuration
369
+ const l1Constants = epochCache.getL1Constants();
370
+ const topicScoreParams = createAllTopicScoreParams(protocolVersion, {
371
+ slotDurationMs: l1Constants.slotDuration * 1000,
372
+ ethereumSlotDuration: l1Constants.ethereumSlotDuration,
373
+ heartbeatIntervalMs: config.gossipsubInterval,
374
+ targetCommitteeSize: l1Constants.targetCommitteeSize,
375
+ blockDurationMs: config.blockDurationMs,
376
+ l1PublishingTime: config.l1PublishingTime,
377
+ p2pPropagationTime: config.attestationPropagationTime,
378
+ expectedBlockProposalsPerSlot: config.expectedBlockProposalsPerSlot,
379
+ });
380
+
277
381
  const node = await createLibp2p({
278
382
  start: false,
279
383
  peerId,
@@ -369,33 +473,18 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
369
473
  scoreParams: createPeerScoreParams({
370
474
  // IPColocation factor can be disabled for local testing - default to -5
371
475
  IPColocationFactorWeight: config.debugDisableColocationPenalty ? 0 : -5.0,
372
- topics: {
373
- [txTopic]: createTopicScoreParams({
374
- topicWeight: 1,
375
- invalidMessageDeliveriesWeight: -20,
376
- invalidMessageDeliveriesDecay: 0.5,
377
- }),
378
- [blockAttestationTopic]: createTopicScoreParams({
379
- topicWeight: 1,
380
- invalidMessageDeliveriesWeight: -20,
381
- invalidMessageDeliveriesDecay: 0.5,
382
- }),
383
- [blockProposalTopic]: createTopicScoreParams({
384
- topicWeight: 1,
385
- invalidMessageDeliveriesWeight: -20,
386
- invalidMessageDeliveriesDecay: 0.5,
387
- }),
388
- },
476
+ topics: topicScoreParams,
389
477
  }),
390
478
  }) as (components: GossipSubComponents) => GossipSub,
391
- components: (components: { connectionManager: ConnectionManager }) => ({
479
+ components: (components: { connectionManager: ConnectionManager; addressManager: AddressManager }) => ({
392
480
  connectionManager: components.connectionManager,
481
+ addressManager: components.addressManager,
393
482
  }),
394
483
  },
395
- logger: createLibp2pComponentLogger(logger.module),
484
+ logger: createLibp2pComponentLogger(logger.module, logger.getBindings()),
396
485
  });
397
486
 
398
- const peerScoring = new PeerScoring(config);
487
+ const peerScoring = new PeerScoring(config, telemetry);
399
488
  const reqresp = new ReqResp(config, node, peerScoring, createLogger(`${logger.module}:reqresp`));
400
489
 
401
490
  const peerManager = new PeerManager(
@@ -411,13 +500,19 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
411
500
  epochCache,
412
501
  );
413
502
 
414
- // Update gossipsub score params
415
- node.services.pubsub.score.params.appSpecificWeight = 10;
503
+ // Gate req/resp data protocols for unauthenticated peers when p2pAllowOnlyValidators is enabled
504
+ reqresp.setShouldRejectPeer(peerId => peerManager.shouldDisableP2PGossip(peerId));
505
+
506
+ // Configure application-specific scoring for gossipsub.
507
+ // The weight scales app score to align with gossipsub thresholds:
508
+ // - Disconnect (-50) × 10 = -500 = gossipThreshold (stops receiving gossip)
509
+ // - Ban (-100) × 10 = -1000 = publishThreshold (cannot publish)
510
+ // Note: positive topic scores can offset penalties, so alignment is best-effort.
511
+ node.services.pubsub.score.params.appSpecificWeight = APP_SPECIFIC_WEIGHT;
416
512
  node.services.pubsub.score.params.appSpecificScore = (peerId: string) =>
417
513
  peerManager.shouldDisableP2PGossip(peerId) ? -Infinity : peerManager.getPeerScore(peerId);
418
514
 
419
515
  return new LibP2PService(
420
- clientType,
421
516
  config,
422
517
  node,
423
518
  peerDiscoveryService,
@@ -428,8 +523,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
428
523
  epochCache,
429
524
  proofVerifier,
430
525
  worldStateSynchronizer,
526
+ blockMinFeesProvider,
431
527
  telemetry,
432
528
  logger,
529
+ txValidationCache,
433
530
  );
434
531
  }
435
532
 
@@ -443,43 +540,29 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
443
540
  throw new Error('P2P service already started');
444
541
  }
445
542
 
446
- // Get listen & announce addresses for logging
447
543
  const { p2pIp, p2pPort } = this.config;
448
- if (!p2pIp) {
449
- throw new Error('Announce address not provided.');
450
- }
451
- const announceTcpMultiaddr = convertToMultiaddr(p2pIp, p2pPort, 'tcp');
452
-
453
- // Start job queue, peer discovery service and libp2p node
454
- this.jobQueue.start();
455
-
456
- await this.peerManager.initializePeers();
457
- if (!this.config.p2pDiscoveryDisabled) {
458
- await this.peerDiscoveryService.start();
459
- }
460
- await this.node.start();
461
-
462
- // Subscribe to standard GossipSub topics by default
463
- for (const topic of getTopicsForClientAndConfig(this.clientType, this.config.disableTransactions)) {
464
- this.subscribeToTopic(this.topicStrings[topic]);
544
+ if (!p2pIp && !this.config.queryForIp) {
545
+ throw new Error('Announce address not provided and queryForIp is not enabled.');
465
546
  }
547
+ const announceTcpMultiaddr = p2pIp ? convertToMultiaddr(p2pIp, p2pPort, 'tcp') : undefined;
466
548
 
467
549
  // Create request response protocol handlers
468
550
  const txHandler = reqRespTxHandler(this.mempools);
469
551
  const goodbyeHandler = reqGoodbyeHandler(this.peerManager);
470
- const blockHandler = reqRespBlockHandler(this.archiver);
471
552
  const statusHandler = reqRespStatusHandler(this.protocolVersion, this.worldStateSynchronizer, this.logger);
472
553
 
473
554
  const requestResponseHandlers: Partial<ReqRespSubProtocolHandlers> = {
474
555
  [ReqRespSubProtocol.PING]: pingHandler,
475
556
  [ReqRespSubProtocol.STATUS]: statusHandler.bind(this),
476
557
  [ReqRespSubProtocol.GOODBYE]: goodbyeHandler.bind(this),
477
- [ReqRespSubProtocol.BLOCK]: blockHandler.bind(this),
478
558
  };
479
559
 
480
- // Only handle block transactions request if attestation pool is available to the client
481
- if (this.mempools.attestationPool && !this.config.disableTransactions) {
482
- const blockTxsHandler = reqRespBlockTxsHandler(this.mempools.attestationPool, this.mempools.txPool);
560
+ if (!this.config.disableTransactions) {
561
+ const blockTxsHandler = reqRespBlockTxsHandler(
562
+ this.mempools.attestationPool,
563
+ this.archiver,
564
+ this.mempools.txPool,
565
+ );
483
566
  requestResponseHandlers[ReqRespSubProtocol.BLOCK_TXS] = blockTxsHandler.bind(this);
484
567
  }
485
568
 
@@ -487,25 +570,65 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
487
570
  requestResponseHandlers[ReqRespSubProtocol.TX] = txHandler.bind(this);
488
571
  }
489
572
 
573
+ await this.peerManager.initializePeers();
574
+
575
+ await this.reqresp.start(requestResponseHandlers);
576
+
577
+ await this.node.start();
578
+
579
+ // Subscribe to standard GossipSub topics by default
580
+ for (const topic of getTopicsForConfig(this.config.disableTransactions)) {
581
+ this.subscribeToTopic(this.topicStrings[topic]);
582
+ }
583
+
490
584
  // add GossipSub listener
491
585
  this.node.services.pubsub.addEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
492
586
 
493
- // Start running promise for peer discovery
587
+ // Start running promise for peer discovery and metrics collection
588
+ if (!this.config.p2pDiscoveryDisabled) {
589
+ await this.peerDiscoveryService.start();
590
+ }
591
+
592
+ // Bridge discv5 IP changes to libp2p's AddressManager so peers see the updated address
593
+ if (this.config.queryForIp) {
594
+ this.discoveredP2pIp = this.config.p2pIp;
595
+ this.logger.info('IP change tracking enabled, bridging discv5 IP updates to libp2p AddressManager');
596
+ this.ipChangedHandler = (ip: string) => {
597
+ const addressManager = this.node.services.components.addressManager;
598
+ const newAddr = multiaddr(convertToMultiaddr(ip, this.config.p2pPort, 'tcp'));
599
+ const previousIp = this.discoveredP2pIp;
600
+
601
+ if (previousIp) {
602
+ const oldAddr = multiaddr(convertToMultiaddr(previousIp, this.config.p2pPort, 'tcp'));
603
+ addressManager.removeObservedAddr(oldAddr);
604
+ this.logger.info('Libp2p announce address updated due to IP change', {
605
+ previousIp,
606
+ newIp: ip,
607
+ newMultiaddr: newAddr.toString(),
608
+ });
609
+ } else {
610
+ this.logger.info('Libp2p announce address set from initial discv5 IP discovery', {
611
+ ip,
612
+ multiaddr: newAddr.toString(),
613
+ });
614
+ }
615
+
616
+ addressManager.addObservedAddr(newAddr);
617
+ addressManager.confirmObservedAddr(newAddr);
618
+ this.discoveredP2pIp = ip;
619
+ };
620
+ this.peerDiscoveryService.on('ip:changed', this.ipChangedHandler);
621
+ }
622
+
494
623
  this.discoveryRunningPromise = new RunningPromise(
495
- () => this.peerManager.heartbeat(),
624
+ async () => {
625
+ await this.peerManager.heartbeat();
626
+ },
496
627
  this.logger,
497
628
  this.config.peerCheckIntervalMS,
498
629
  );
499
630
  this.discoveryRunningPromise.start();
500
631
 
501
- // Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
502
- const reqrespSubProtocolValidators = {
503
- ...DEFAULT_SUB_PROTOCOL_VALIDATORS,
504
- // TODO(#11336): A request validator for blocks
505
- [ReqRespSubProtocol.TX]: this.validateRequestedTxs.bind(this),
506
- [ReqRespSubProtocol.BLOCK_TXS]: this.validateRequestedBlockTxs.bind(this),
507
- };
508
- await this.reqresp.start(requestResponseHandlers, reqrespSubProtocolValidators);
509
632
  this.logger.info(`Started P2P service`, {
510
633
  listen: this.config.listenAddress,
511
634
  port: this.config.p2pPort,
@@ -522,12 +645,14 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
522
645
  // Remove gossip sub listener
523
646
  this.node.services.pubsub.removeEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
524
647
 
648
+ if (this.ipChangedHandler) {
649
+ this.peerDiscoveryService.removeListener('ip:changed', this.ipChangedHandler);
650
+ this.ipChangedHandler = undefined;
651
+ }
652
+
525
653
  // Stop peer manager
526
654
  this.logger.debug('Stopping peer manager...');
527
655
  await this.peerManager.stop();
528
-
529
- this.logger.debug('Stopping job queue...');
530
- await this.jobQueue.end();
531
656
  this.logger.debug('Stopping running promise...');
532
657
  await this.discoveryRunningPromise?.stop();
533
658
  this.logger.debug('Stopping peer discovery service...');
@@ -539,12 +664,8 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
539
664
  this.logger.info('LibP2P service stopped');
540
665
  }
541
666
 
542
- addReqRespSubProtocol(
543
- subProtocol: ReqRespSubProtocol,
544
- handler: ReqRespSubProtocolHandler,
545
- validator?: ReqRespSubProtocolValidators[ReqRespSubProtocol],
546
- ): Promise<void> {
547
- return this.reqresp.addSubProtocol(subProtocol, handler, validator);
667
+ addReqRespSubProtocol(subProtocol: ReqRespSubProtocol, handler: ReqRespSubProtocolHandler): Promise<void> {
668
+ return this.reqresp.addSubProtocol(subProtocol, handler);
548
669
  }
549
670
 
550
671
  public registerThisValidatorAddresses(address: EthAddress[]): void {
@@ -555,6 +676,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
555
676
  return this.peerManager.getPeers(includePending);
556
677
  }
557
678
 
679
+ public getGossipMeshPeerCount(topicType: TopicType): number {
680
+ return this.node.services.pubsub.getMeshPeers(this.topicStrings[topicType]).length;
681
+ }
682
+
558
683
  private handleGossipSubEvent(e: CustomEvent<GossipsubMessage>) {
559
684
  this.logger.trace(`Received PUBSUB message.`);
560
685
 
@@ -568,18 +693,13 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
568
693
  setImmediate(() => void safeJob());
569
694
  }
570
695
 
571
- /**
572
- * Send a batch of requests to peers, and return the responses
573
- * @param protocol - The request response protocol to use
574
- * @param requests - The requests to send to the peers
575
- * @returns The responses to the requests
576
- */
577
- sendBatchRequest<SubProtocol extends ReqRespSubProtocol>(
578
- protocol: SubProtocol,
579
- requests: InstanceType<SubProtocolMap[SubProtocol]['request']>[],
580
- pinnedPeerId: PeerId | undefined,
581
- ): Promise<InstanceType<SubProtocolMap[SubProtocol]['response']>[]> {
582
- return this.reqresp.sendBatchRequest(protocol, requests, pinnedPeerId);
696
+ public sendRequestToPeer(
697
+ peerId: PeerId,
698
+ subProtocol: ReqRespSubProtocol,
699
+ payload: Buffer,
700
+ dialTimeout?: number,
701
+ ): Promise<ReqRespResponse> {
702
+ return this.reqresp.sendRequestToPeer(peerId, subProtocol, payload, dialTimeout);
583
703
  }
584
704
 
585
705
  /**
@@ -594,6 +714,37 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
594
714
  this.blockReceivedCallback = callback;
595
715
  }
596
716
 
717
+ public registerValidatorCheckpointReceivedCallback(callback: P2PCheckpointReceivedCallback) {
718
+ this.validatorCheckpointReceivedCallback = callback;
719
+ }
720
+
721
+ public registerAllNodesCheckpointReceivedCallback(callback: P2PCheckpointReceivedCallback) {
722
+ this.allNodesCheckpointReceivedCallback = callback;
723
+ }
724
+
725
+ /**
726
+ * Registers a callback to be invoked when a duplicate proposal is detected.
727
+ * This callback is triggered on the first duplicate (when count goes from 1 to 2).
728
+ */
729
+ public registerDuplicateProposalCallback(
730
+ callback: (info: { slot: SlotNumber; proposer: EthAddress; type: 'checkpoint' | 'block' }) => void,
731
+ ): void {
732
+ this.duplicateProposalCallback = callback;
733
+ }
734
+
735
+ /**
736
+ * Registers a callback to be invoked when a duplicate attestation is detected.
737
+ * A validator signing attestations for different proposals at the same slot.
738
+ * This callback is triggered on the first duplicate (when count goes from 1 to 2).
739
+ */
740
+ public registerDuplicateAttestationCallback(callback: P2PDuplicateAttestationCallback): void {
741
+ this.duplicateAttestationCallback = callback;
742
+ }
743
+
744
+ public registerCheckpointAttestationCallback(callback: P2PCheckpointAttestationCallback): void {
745
+ this.checkpointAttestationCallback = callback;
746
+ }
747
+
597
748
  /**
598
749
  * Subscribes to a topic.
599
750
  * @param topic - The topic to subscribe to.
@@ -615,7 +766,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
615
766
  if (!this.node.services.pubsub) {
616
767
  throw new Error('Pubsub service not available.');
617
768
  }
618
- const p2pMessage = P2PMessage.fromGossipable(message);
769
+ const isBlockProposal = topic === this.topicStrings[TopicType.block_proposal];
770
+ const traceContext =
771
+ this.config.debugP2PInstrumentMessages && isBlockProposal ? this.telemetry.getTraceContext() : undefined;
772
+ const p2pMessage = P2PMessage.fromGossipable(message, this.config.debugP2PInstrumentMessages, traceContext);
619
773
  const result = await this.node.services.pubsub.publish(topic, p2pMessage.toMessageData());
620
774
  return result.recipients.length;
621
775
  }
@@ -636,12 +790,15 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
636
790
  case this.topicStrings[TopicType.tx]:
637
791
  topicType = TopicType.tx;
638
792
  break;
639
- case this.topicStrings[TopicType.block_attestation]:
640
- topicType = TopicType.block_attestation;
641
- break;
642
793
  case this.topicStrings[TopicType.block_proposal]:
643
794
  topicType = TopicType.block_proposal;
644
795
  break;
796
+ case this.topicStrings[TopicType.checkpoint_proposal]:
797
+ topicType = TopicType.checkpoint_proposal;
798
+ break;
799
+ case this.topicStrings[TopicType.checkpoint_attestation]:
800
+ topicType = TopicType.checkpoint_attestation;
801
+ break;
645
802
  default:
646
803
  this.logger.error(`Received message on unknown topic: ${msg.topic}`);
647
804
  break;
@@ -652,6 +809,9 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
652
809
  if (!validator || !validator.addMessage(msgId)) {
653
810
  this.instrumentation.incMessagePrevalidationStatus(false, topicType);
654
811
  this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Ignore);
812
+ if (topicType === TopicType.tx) {
813
+ this.logger.verbose(`Ignoring already-seen tx gossip message`, { msgId, source: source.toString() });
814
+ }
655
815
  return { result: false, topicType };
656
816
  }
657
817
 
@@ -660,13 +820,39 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
660
820
  return { result: true, topicType };
661
821
  }
662
822
 
823
+ /**
824
+ * Safely deserializes a P2PMessage from raw message data.
825
+ * @param msgId - The message ID.
826
+ * @param source - The peer ID of the message source.
827
+ * @param data - The raw message data.
828
+ * @returns The deserialized P2PMessage or undefined if deserialization fails.
829
+ */
830
+ private safelyDeserializeP2PMessage(msgId: string, source: PeerId, data: Uint8Array): P2PMessage | undefined {
831
+ try {
832
+ return P2PMessage.fromMessageData(Buffer.from(data), this.config.debugP2PInstrumentMessages);
833
+ } catch (err) {
834
+ this.logger.error(`Error deserializing P2PMessage`, err, {
835
+ msgId,
836
+ source: source.toString(),
837
+ });
838
+ this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Reject);
839
+ this.peerManager.penalizePeer(source, PeerErrorSeverity.LowToleranceError);
840
+ return undefined;
841
+ }
842
+ }
843
+
663
844
  /**
664
845
  * Handles a new gossip message that was received by the client.
665
846
  * @param topic - The message's topic.
666
847
  * @param data - The message data
667
848
  */
668
849
  protected async handleNewGossipMessage(msg: Message, msgId: string, source: PeerId) {
669
- const p2pMessage = P2PMessage.fromMessageData(Buffer.from(msg.data));
850
+ const msgReceivedTime = Date.now();
851
+ let topicType: TopicType | undefined;
852
+ const p2pMessage = this.safelyDeserializeP2PMessage(msgId, source, msg.data);
853
+ if (!p2pMessage) {
854
+ return;
855
+ }
670
856
 
671
857
  const preValidationResult = this.preValidateReceivedMessage(msg, msgId, source);
672
858
 
@@ -674,63 +860,226 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
674
860
  return;
675
861
  }
676
862
 
863
+ // Determine topic type for attributes
677
864
  if (msg.topic === this.topicStrings[TopicType.tx]) {
678
- await this.handleGossipedTx(p2pMessage.payload, msgId, source);
865
+ topicType = TopicType.tx;
866
+ } else if (msg.topic === this.topicStrings[TopicType.checkpoint_attestation]) {
867
+ topicType = TopicType.checkpoint_attestation;
868
+ } else if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
869
+ topicType = TopicType.block_proposal;
870
+ } else if (msg.topic === this.topicStrings[TopicType.checkpoint_proposal]) {
871
+ topicType = TopicType.checkpoint_proposal;
679
872
  }
680
- if (msg.topic === this.topicStrings[TopicType.block_attestation] && this.clientType === P2PClientType.Full) {
681
- await this.processAttestationFromPeer(p2pMessage.payload, msgId, source);
873
+
874
+ // Process the message, optionally within a linked span for trace propagation
875
+ const processMessage = async () => {
876
+ if (
877
+ this.config.skipIncomingProposals &&
878
+ (msg.topic === this.topicStrings[TopicType.block_proposal] ||
879
+ msg.topic === this.topicStrings[TopicType.checkpoint_proposal])
880
+ ) {
881
+ this.logger.warn(`Ignoring incoming proposal (skipIncomingProposals is set)`, { topic: msg.topic });
882
+ this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), TopicValidatorResult.Ignore);
883
+ return;
884
+ }
885
+ if (msg.topic === this.topicStrings[TopicType.tx]) {
886
+ await this.handleGossipedTx(p2pMessage.payload, msgId, source);
887
+ } else if (msg.topic === this.topicStrings[TopicType.checkpoint_attestation]) {
888
+ await this.processCheckpointAttestationFromPeer(p2pMessage.payload, msgId, source);
889
+ } else if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
890
+ await this.processBlockFromPeer(p2pMessage.payload, msgId, source);
891
+ } else if (msg.topic === this.topicStrings[TopicType.checkpoint_proposal]) {
892
+ await this.handleGossipedCheckpointProposal(p2pMessage.payload, msgId, source);
893
+ } else {
894
+ this.logger.error(`Received message on unknown topic: ${msg.topic}`);
895
+ }
896
+ };
897
+
898
+ const latency = p2pMessage.timestamp !== undefined ? msgReceivedTime - p2pMessage.timestamp.getTime() : undefined;
899
+ const propagatedContext = p2pMessage.traceContext
900
+ ? this.telemetry.extractPropagatedContext(p2pMessage.traceContext)
901
+ : undefined;
902
+
903
+ if (propagatedContext) {
904
+ await this.tracer.startActiveSpan(
905
+ 'LibP2PService.processMessage',
906
+ {
907
+ attributes: {
908
+ [Attributes.TOPIC_NAME]: topicType!,
909
+ [Attributes.PEER_ID]: source.toString(),
910
+ },
911
+ },
912
+ propagatedContext,
913
+ async span => {
914
+ try {
915
+ await processMessage();
916
+ span.setStatus({
917
+ code: SpanStatusCode.OK,
918
+ });
919
+ } catch (err) {
920
+ span.setStatus({
921
+ code: SpanStatusCode.ERROR,
922
+ message: String(err),
923
+ });
924
+ if (typeof err === 'string' || (err && err instanceof Error)) {
925
+ span.recordException(err);
926
+ }
927
+ throw err;
928
+ } finally {
929
+ span.end();
930
+ }
931
+ },
932
+ );
933
+ } else {
934
+ await processMessage();
682
935
  }
683
- if (msg.topic === this.topicStrings[TopicType.block_proposal]) {
684
- await this.processBlockFromPeer(p2pMessage.payload, msgId, source);
936
+
937
+ if (latency !== undefined && topicType !== undefined) {
938
+ this.instrumentation.recordMessageLatency(topicType, latency);
685
939
  }
686
940
 
687
941
  return;
688
942
  }
689
943
 
690
- protected async validateReceivedMessage<T>(
691
- validationFunc: () => Promise<ReceivedMessageValidationResult<T>>,
944
+ protected async validateReceivedMessage<T, M = undefined>(
945
+ validationFunc: () => Promise<ReceivedMessageValidationResult<T, M>>,
692
946
  msgId: string,
693
947
  source: PeerId,
694
948
  topicType: TopicType,
695
- ): Promise<ReceivedMessageValidationResult<T>> {
696
- let resultAndObj: ReceivedMessageValidationResult<T> = { result: TopicValidatorResult.Reject };
949
+ ): Promise<ReceivedMessageValidationResult<T, M>> {
950
+ // Default to reject result with a penalty if validation function throws an error
951
+ let resultAndObj: ReceivedMessageValidationResult<T, M> = {
952
+ result: TopicValidatorResult.Reject,
953
+ severity: PeerErrorSeverity.MidToleranceError,
954
+ };
697
955
  const timer = new Timer();
698
956
  try {
699
957
  resultAndObj = await validationFunc();
700
958
  } catch (err) {
701
- this.logger.error(`Error deserializing and validating gossipsub message`, err, {
702
- msgId,
703
- source: source.toString(),
704
- topicType,
705
- });
959
+ this.logger.error(`Error validating gossipsub message`, err, { msgId, source: source.toString(), topicType });
960
+ }
961
+
962
+ const validationTimeMs = timer.ms();
963
+ const mcacheWindowMs = this.config.gossipsubMcacheLength * this.config.gossipsubInterval;
964
+ if (validationTimeMs > mcacheWindowMs * 0.75) {
965
+ this.instrumentation.incSlowValidation(topicType);
966
+ this.logger.warn(
967
+ `Gossip validation for ${topicType} took ${validationTimeMs}ms, approaching mcache eviction window of ${mcacheWindowMs}ms. ` +
968
+ `Message forwarding may be skipped if validation exceeds the window.`,
969
+ { msgId, source: source.toString(), topicType, validationTimeMs, mcacheWindowMs },
970
+ );
706
971
  }
707
972
 
708
973
  if (resultAndObj.result === TopicValidatorResult.Accept) {
974
+ this.logger.debug(`Message ${topicType} accepted by validator`, { msgId, source: source.toString(), topicType });
709
975
  this.instrumentation.recordMessageValidation(topicType, timer);
976
+ } else if (resultAndObj.result === TopicValidatorResult.Reject) {
977
+ this.logger.warn(`Message ${topicType} rejected by validator with severity ${resultAndObj.severity}`, {
978
+ msgId,
979
+ source: source.toString(),
980
+ topicType,
981
+ severity: resultAndObj.severity,
982
+ });
983
+ this.peerManager.penalizePeer(source, resultAndObj.severity);
984
+ } else {
985
+ this.logger.trace(`Message ${topicType} ignored by validator`, { msgId, source: source.toString(), topicType });
710
986
  }
711
987
 
712
988
  this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), resultAndObj.result);
713
989
  return resultAndObj;
714
990
  }
715
991
 
992
+ private tryDeserialize<T>(deserializeFunc: () => T, msgId: string, source: PeerId): T | undefined {
993
+ try {
994
+ return deserializeFunc();
995
+ } catch (err) {
996
+ this.logger.warn(`Failed to deserialize gossipsub message from buffer`, {
997
+ err,
998
+ msgId,
999
+ source: source.toString(),
1000
+ });
1001
+ return undefined;
1002
+ }
1003
+ }
1004
+
716
1005
  protected async handleGossipedTx(payloadData: Buffer, msgId: string, source: PeerId) {
717
1006
  const validationFunc: () => Promise<ReceivedMessageValidationResult<Tx>> = async () => {
718
- const tx = Tx.fromBuffer(payloadData);
719
- const isValid = await this.validatePropagatedTx(tx, source);
720
- const exists = isValid && (await this.mempools.txPool.hasTx(tx.getTxHash()));
1007
+ const tx = this.tryDeserialize(() => Tx.fromBuffer(payloadData), msgId, source);
1008
+ if (!tx) {
1009
+ return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.LowToleranceError };
1010
+ }
1011
+
1012
+ const currentBlockNumber = await this.archiver.getBlockNumber();
1013
+ const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
1014
+
1015
+ // Stage 1: fast validators (metadata, data, timestamps, double-spend, gas, phases, block header)
1016
+ const firstStageValidators = await this.createFirstStageMessageValidators(currentBlockNumber, nextSlotTimestamp);
1017
+ const firstStageOutcome = await this.runValidations(tx, firstStageValidators);
1018
+ if (!firstStageOutcome.allPassed) {
1019
+ const { name } = firstStageOutcome.failure;
1020
+ let { severity } = firstStageOutcome.failure;
1021
+
1022
+ // Double spend validator has a special case handler. We perform more detailed examination
1023
+ // as to how recently the nullifier was entered into the tree and if the transaction should
1024
+ // have 'known' the nullifier existed. This determines the severity of the penalty applied to the peer.
1025
+ if (name === 'doubleSpendValidator') {
1026
+ const txBlockNumber = BlockNumber(currentBlockNumber + 1);
1027
+ severity = await this.handleDoubleSpendFailure(tx, txBlockNumber);
1028
+ }
1029
+
1030
+ this.logger.verbose(`Rejecting gossiped tx ${tx.getTxHash().toString()}: stage 1 validation failed`, {
1031
+ validator: name,
1032
+ severity,
1033
+ source: source.toString(),
1034
+ });
1035
+ return { result: TopicValidatorResult.Reject, severity };
1036
+ }
1037
+
1038
+ // Pool pre-check: see if the pool would accept this tx before doing expensive proof verification
1039
+ const canAdd = await this.mempools.txPool.canAddPendingTx(tx);
1040
+ if (canAdd === 'ignored') {
1041
+ this.logger.verbose(`Ignoring gossiped tx ${tx.getTxHash().toString()}: pool pre-check returned ignored`, {
1042
+ source: source.toString(),
1043
+ });
1044
+ return { result: TopicValidatorResult.Ignore, obj: tx };
1045
+ }
1046
+
1047
+ // Stage 2: expensive proof verification
1048
+ const secondStageValidators = this.createSecondStageMessageValidators();
1049
+ const secondStageOutcome = await this.runValidations(tx, secondStageValidators);
1050
+ if (!secondStageOutcome.allPassed) {
1051
+ const { severity, name } = secondStageOutcome.failure;
1052
+ this.logger.verbose(`Rejecting gossiped tx ${tx.getTxHash().toString()}: stage 2 validation failed`, {
1053
+ validator: name,
1054
+ severity,
1055
+ source: source.toString(),
1056
+ });
1057
+ return { result: TopicValidatorResult.Reject, severity };
1058
+ }
1059
+
1060
+ // Pool add: persist the tx
1061
+ const txHash = tx.getTxHash();
1062
+ const addResult = await this.mempools.txPool.addPendingTxs([tx], { source: 'gossip' });
1063
+
1064
+ const wasAccepted = addResult.accepted.some(h => h.equals(txHash));
1065
+ const wasIgnored = addResult.ignored.some(h => h.equals(txHash));
721
1066
 
722
- this.logger.trace(`Validate propagated tx`, {
723
- isValid,
724
- exists,
1067
+ this.logger.verbose(`Validate propagated tx ${txHash.toString()}`, {
1068
+ wasAccepted,
1069
+ wasIgnored,
725
1070
  [Attributes.P2P_ID]: source.toString(),
726
1071
  });
727
1072
 
728
- if (!isValid) {
729
- return { result: TopicValidatorResult.Reject };
730
- } else if (exists) {
1073
+ if (wasAccepted) {
1074
+ return { result: TopicValidatorResult.Accept, obj: tx };
1075
+ } else if (wasIgnored) {
731
1076
  return { result: TopicValidatorResult.Ignore, obj: tx };
732
1077
  } else {
733
- return { result: TopicValidatorResult.Accept, obj: tx };
1078
+ this.logger.warn(`Gossiped tx ${txHash.toString()} unexpectedly rejected by pool`, {
1079
+ source: source.toString(),
1080
+ txHash: txHash.toString(),
1081
+ });
1082
+ return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.HighToleranceError };
734
1083
  }
735
1084
  };
736
1085
 
@@ -739,6 +1088,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
739
1088
  return;
740
1089
  }
741
1090
 
1091
+ // Tx was accepted into pool and will be propagated - just log and record metrics
742
1092
  const txHash = tx.getTxHash();
743
1093
  const txHashString = txHash.toString();
744
1094
  this.logger.verbose(`Received tx ${txHashString} from external peer ${source.toString()} via gossip`, {
@@ -746,155 +1096,419 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
746
1096
  txHash: txHashString,
747
1097
  });
748
1098
 
749
- if (this.config.dropTransactions && randomInt(1000) < this.config.dropTransactionsProbability * 1000) {
750
- this.logger.warn(`Intentionally dropping tx ${txHashString} (probability rule)`);
751
- return;
752
- }
753
-
754
- await this.mempools.txPool.addTxs([tx]);
1099
+ this.instrumentation.incrementTxReceived(1);
755
1100
  }
756
1101
 
757
1102
  /**
758
- * Process Attestation From Peer
759
- * When a proposal is received from a peer, we add it to the attestation pool, so it can be accessed by other services.
760
- *
761
- * @param attestation - The attestation to process.
1103
+ * Process a checkpoint attestation from a peer.
1104
+ * Validates the attestation and adds it to the pool.
762
1105
  */
763
- private async processAttestationFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
764
- const validationFunc: () => Promise<ReceivedMessageValidationResult<BlockAttestation>> = async () => {
765
- const attestation = BlockAttestation.fromBuffer(payloadData);
766
- const isValid = await this.validateAttestation(source, attestation);
767
- const exists = isValid && (await this.mempools.attestationPool!.hasAttestation(attestation));
768
-
769
- this.logger.trace(`Validate propagated block attestation`, {
770
- isValid,
771
- exists,
772
- [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString(),
773
- [Attributes.P2P_ID]: source.toString(),
774
- });
775
-
776
- if (!isValid) {
777
- return { result: TopicValidatorResult.Reject };
778
- } else if (exists) {
779
- return { result: TopicValidatorResult.Ignore, obj: attestation };
780
- } else {
781
- return { result: TopicValidatorResult.Accept, obj: attestation };
782
- }
783
- };
784
-
785
- const { result, obj: attestation } = await this.validateReceivedMessage<BlockAttestation>(
786
- validationFunc,
1106
+ private async processCheckpointAttestationFromPeer(
1107
+ payloadData: Buffer,
1108
+ msgId: string,
1109
+ source: PeerId,
1110
+ ): Promise<void> {
1111
+ const { result, obj: attestation } = await this.validateReceivedMessage<CheckpointAttestation>(
1112
+ () => {
1113
+ const attestation = this.tryDeserialize(() => CheckpointAttestation.fromBuffer(payloadData), msgId, source);
1114
+ if (!attestation) {
1115
+ return Promise.resolve({
1116
+ result: TopicValidatorResult.Reject,
1117
+ severity: PeerErrorSeverity.LowToleranceError,
1118
+ });
1119
+ }
1120
+ return this.validateAndStoreCheckpointAttestation(source, attestation);
1121
+ },
787
1122
  msgId,
788
1123
  source,
789
- TopicType.block_attestation,
1124
+ TopicType.checkpoint_attestation,
790
1125
  );
791
1126
 
792
1127
  if (result !== TopicValidatorResult.Accept || !attestation) {
793
1128
  return;
794
1129
  }
795
1130
 
796
- this.logger.debug(
797
- `Received attestation for slot ${attestation.slotNumber.toNumber()} from external peer ${source.toString()}`,
1131
+ this.logger.verbose(
1132
+ `Received valid checkpoint attestation for slot ${attestation.slotNumber} from external peer ${source.toString()}`,
798
1133
  {
799
- p2pMessageIdentifier: await attestation.p2pMessageIdentifier(),
800
- slot: attestation.slotNumber.toNumber(),
1134
+ p2pMessageIdentifier: await attestation.p2pMessageLoggingIdentifier(),
1135
+ slot: attestation.slotNumber,
801
1136
  archive: attestation.archive.toString(),
802
1137
  source: source.toString(),
803
1138
  },
804
1139
  );
805
-
806
- await this.mempools.attestationPool!.addAttestations([attestation]);
807
1140
  }
808
1141
 
809
- private async processBlockFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
810
- const validationFunc: () => Promise<ReceivedMessageValidationResult<BlockProposal>> = async () => {
811
- const block = BlockProposal.fromBuffer(payloadData);
812
- const isValid = await this.validateBlockProposal(source, block);
1142
+ /** Validates a checkpoint attestation and adds it to the pool. Penalizes the peer if validation fails. */
1143
+ @trackSpan('Libp2pService.validateAndStoreCheckpointAttestation', (_peerId, attestation) => ({
1144
+ [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toString(),
1145
+ }))
1146
+ protected async validateAndStoreCheckpointAttestation(
1147
+ peerId: PeerId,
1148
+ attestation: CheckpointAttestation,
1149
+ ): Promise<ReceivedMessageValidationResult<CheckpointAttestation>> {
1150
+ const validationResult = await this.checkpointAttestationValidator.validate(attestation);
813
1151
 
814
- // Note that we dont have an attestation pool if we're a prover node, but we still
815
- // subscribe to block proposal topics in order to prevent their txs from being cleared.
816
- const exists = isValid && (await this.mempools.attestationPool?.hasBlockProposal(block));
1152
+ if (validationResult.result === 'reject') {
1153
+ this.logger.warn(`Penalizing peer ${peerId} for checkpoint attestation validation failure`);
1154
+ return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
1155
+ }
817
1156
 
818
- this.logger.trace(`Validate propagated block proposal`, {
819
- isValid,
820
- exists,
821
- [Attributes.SLOT_NUMBER]: block.payload.header.slotNumber.toString(),
822
- [Attributes.P2P_ID]: source.toString(),
1157
+ if (validationResult.result === 'ignore') {
1158
+ return { result: TopicValidatorResult.Ignore, obj: attestation };
1159
+ }
1160
+
1161
+ // Try to add the attestation: this handles existence check, cap check, and adding in one call
1162
+ // count is the number of attestations by this signer for this slot (for duplicate detection)
1163
+ const slot = attestation.payload.header.slotNumber;
1164
+ const { added, alreadyExists, count } =
1165
+ await this.mempools.attestationPool.tryAddCheckpointAttestation(attestation);
1166
+
1167
+ this.logger.trace(`Validate propagated checkpoint attestation`, {
1168
+ added,
1169
+ alreadyExists,
1170
+ count,
1171
+ [Attributes.SLOT_NUMBER]: slot.toString(),
1172
+ [Attributes.P2P_ID]: peerId.toString(),
1173
+ });
1174
+
1175
+ // Exact same attestation received, no need to re-broadcast
1176
+ if (alreadyExists) {
1177
+ return { result: TopicValidatorResult.Ignore, obj: attestation };
1178
+ }
1179
+
1180
+ // Could not add (cap reached for signer), penalize and do not re-broadcast
1181
+ if (!added) {
1182
+ this.logger.warn(`Rejecting checkpoint attestation due to cap`, {
1183
+ slot: slot.toString(),
1184
+ archive: attestation.archive.toString(),
1185
+ source: peerId.toString(),
1186
+ attester: attestation.getSender()?.toString(),
1187
+ count,
823
1188
  });
1189
+ return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.HighToleranceError };
1190
+ }
824
1191
 
825
- if (!isValid) {
826
- return { result: TopicValidatorResult.Reject };
827
- } else if (exists) {
828
- return { result: TopicValidatorResult.Ignore, obj: block };
829
- } else {
830
- return { result: TopicValidatorResult.Accept, obj: block };
1192
+ // Check if this is a duplicate attestation (signer attested to a different proposal at the same slot)
1193
+ // count is the number of attestations by this signer for this slot
1194
+ if (count === 2) {
1195
+ const attester = attestation.getSender();
1196
+ if (attester) {
1197
+ this.logger.warn(`Detected duplicate attestation (equivocation) at slot ${slot}`, {
1198
+ slot: slot.toString(),
1199
+ archive: attestation.archive.toString(),
1200
+ source: peerId.toString(),
1201
+ attester: attester.toString(),
1202
+ });
1203
+ this.duplicateAttestationCallback?.({ slot, attester });
831
1204
  }
832
- };
1205
+ }
1206
+
1207
+ // Attestation was added successfully - accept it so other nodes can also detect the equivocation
1208
+ this.checkpointAttestationCallback?.(attestation);
1209
+ return { result: TopicValidatorResult.Accept, obj: attestation };
1210
+ }
833
1211
 
834
- const { result, obj: block } = await this.validateReceivedMessage<BlockProposal>(
835
- validationFunc,
1212
+ protected async processBlockFromPeer(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
1213
+ const {
1214
+ result,
1215
+ obj: block,
1216
+ metadata: { isEquivocated } = {},
1217
+ } = await this.validateReceivedMessage<BlockProposal, { isEquivocated: boolean }>(
1218
+ () => this.validateAndStoreBlockProposal(source, BlockProposal.fromBuffer(payloadData)),
836
1219
  msgId,
837
1220
  source,
838
1221
  TopicType.block_proposal,
839
1222
  );
840
1223
 
841
- if (!result || !block) {
1224
+ // If not accepted or equivocated, return
1225
+ if (result !== TopicValidatorResult.Accept || !block || isEquivocated) {
842
1226
  return;
843
1227
  }
844
1228
 
845
1229
  await this.processValidBlockProposal(block, source);
846
1230
  }
847
1231
 
848
- // REVIEW: callback pattern https://github.com/AztecProtocol/aztec-packages/issues/7963
1232
+ /** Validates a block proposal. Triggers a penalization to the peer that sent it if invalid. Adds to the mempool if valid. */
1233
+ @trackSpan('Libp2pService.validateAndStoreBlockProposal', (_peerId, block) => ({
1234
+ [Attributes.BLOCK_NUMBER]: block.blockNumber.toString(),
1235
+ [Attributes.SLOT_NUMBER]: block.slotNumber.toString(),
1236
+ }))
1237
+ protected async validateAndStoreBlockProposal(
1238
+ peerId: PeerId,
1239
+ block: BlockProposal,
1240
+ ): Promise<ReceivedMessageValidationResult<BlockProposal, { isEquivocated: boolean }>> {
1241
+ const validationResult = await this.blockProposalValidator.validate(block);
1242
+
1243
+ if (validationResult.result === 'reject') {
1244
+ this.logger.warn(`Penalizing peer ${peerId} for block proposal validation failure`);
1245
+ return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
1246
+ }
1247
+
1248
+ if (validationResult.result === 'ignore') {
1249
+ return { result: TopicValidatorResult.Ignore, obj: block };
1250
+ }
1251
+
1252
+ // Try to add the proposal: this handles existence check, cap check, and adding in one call
1253
+ const { added, alreadyExists, count } = await this.mempools.attestationPool.tryAddBlockProposal(block);
1254
+ const isEquivocated = count !== undefined && count > 1;
1255
+
1256
+ // Duplicate proposal received, no need to re-broadcast
1257
+ if (alreadyExists) {
1258
+ this.logger.debug(`Ignoring duplicate block proposal received`, {
1259
+ ...block.toBlockInfo(),
1260
+ indexWithinCheckpoint: block.indexWithinCheckpoint,
1261
+ proposer: block.getSender()?.toString(),
1262
+ source: peerId.toString(),
1263
+ });
1264
+ return { result: TopicValidatorResult.Ignore, obj: block, metadata: { isEquivocated } };
1265
+ }
1266
+
1267
+ // Too many blocks received for this slot and index, penalize peer and do not re-broadcast
1268
+ if (!added) {
1269
+ this.logger.warn(`Penalizing peer for block proposal exceeding per-position cap`, {
1270
+ ...block.toBlockInfo(),
1271
+ indexWithinCheckpoint: block.indexWithinCheckpoint,
1272
+ count,
1273
+ proposer: block.getSender()?.toString(),
1274
+ source: peerId.toString(),
1275
+ });
1276
+ return {
1277
+ result: TopicValidatorResult.Reject,
1278
+ metadata: { isEquivocated },
1279
+ severity: PeerErrorSeverity.HighToleranceError,
1280
+ };
1281
+ }
1282
+
1283
+ // If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
1284
+ // and do re-broadcast it so other nodes in the network know to slash the proposer
1285
+ if (isEquivocated) {
1286
+ const proposer = block.getSender();
1287
+ this.logger.warn(`Detected duplicate block proposal (equivocation) at slot ${block.slotNumber}`, {
1288
+ ...block.toBlockInfo(),
1289
+ source: peerId.toString(),
1290
+ proposer: proposer?.toString(),
1291
+ });
1292
+ // Invoke the duplicate callback on the first duplicate spotted only
1293
+ if (proposer && count === 2) {
1294
+ this.duplicateProposalCallback?.({ slot: block.slotNumber, proposer, type: 'block' });
1295
+ }
1296
+ return { result: TopicValidatorResult.Accept, obj: block, metadata: { isEquivocated } };
1297
+ }
1298
+
1299
+ // Otherwise, we're good to go!
1300
+ return { result: TopicValidatorResult.Accept, obj: block };
1301
+ }
1302
+
1303
+ // REFACTOR(palla): This method should be moved to the p2p_client or to a separate component,
1304
+ // should not be here as it does not deal with p2p networking.
849
1305
  @trackSpan('Libp2pService.processValidBlockProposal', async block => ({
850
- [Attributes.SLOT_NUMBER]: block.slotNumber.toNumber(),
1306
+ [Attributes.SLOT_NUMBER]: block.slotNumber,
851
1307
  [Attributes.BLOCK_ARCHIVE]: block.archive.toString(),
852
- [Attributes.P2P_ID]: await block.p2pMessageIdentifier().then(i => i.toString()),
1308
+ [Attributes.P2P_ID]: await block.p2pMessageLoggingIdentifier().then(i => i.toString()),
853
1309
  }))
854
- private async processValidBlockProposal(block: BlockProposal, sender: PeerId) {
855
- const slot = block.slotNumber.toBigInt();
856
- const previousSlot = slot - 1n;
1310
+ protected async processValidBlockProposal(block: BlockProposal, sender: PeerId) {
1311
+ const slot = block.slotNumber;
857
1312
  this.logger.verbose(`Received block proposal for slot ${slot} from external peer ${sender.toString()}.`, {
858
- p2pMessageIdentifier: await block.p2pMessageIdentifier(),
859
- slot: block.slotNumber.toNumber(),
860
- archive: block.archive.toString(),
1313
+ p2pMessageIdentifier: await block.p2pMessageLoggingIdentifier(),
861
1314
  source: sender.toString(),
1315
+ ...block.toBlockInfo(),
862
1316
  });
863
- const attestationsForPreviousSlot = await this.mempools.attestationPool?.getAttestationsForSlot(previousSlot);
864
- if (attestationsForPreviousSlot !== undefined) {
865
- this.logger.verbose(`Received ${attestationsForPreviousSlot.length} attestations for slot ${previousSlot}`);
1317
+
1318
+ // Mark the txs in this proposal as protected
1319
+ await this.mempools.txPool.protectTxs(block.txHashes, block.blockHeader);
1320
+
1321
+ // Call the block received callback to validate the proposal.
1322
+ // Note: Validators do NOT attest to individual blocks, only to checkpoint proposals.
1323
+ const isValid = await this.blockReceivedCallback(block, sender);
1324
+ if (!isValid) {
1325
+ this.logger.info(`Block proposal validation failed for block ${block.blockNumber}`, block.toBlockInfo());
1326
+ }
1327
+ }
1328
+
1329
+ /**
1330
+ * Handle a gossiped checkpoint proposal.
1331
+ * Validates and processes the checkpoint proposal, then triggers the callback for attestation.
1332
+ */
1333
+ protected async handleGossipedCheckpointProposal(payloadData: Buffer, msgId: string, source: PeerId): Promise<void> {
1334
+ const {
1335
+ result,
1336
+ obj: checkpoint,
1337
+ metadata: { isEquivocated, processBlock } = {},
1338
+ } = await this.validateReceivedMessage<CheckpointProposal, { isEquivocated: boolean; processBlock: boolean }>(
1339
+ () => this.validateAndStoreCheckpointProposal(source, CheckpointProposal.fromBuffer(payloadData)),
1340
+ msgId,
1341
+ source,
1342
+ TopicType.checkpoint_proposal,
1343
+ );
1344
+
1345
+ // Process checkpoint proposal if valid and not equivocated.
1346
+ const processCheckpointFn = () =>
1347
+ result === TopicValidatorResult.Accept && checkpoint && !isEquivocated
1348
+ ? this.processValidCheckpointProposal(checkpoint.toCore(), source)
1349
+ : Promise.resolve();
1350
+
1351
+ // If the checkpoint contained a valid last block, we process it even if the checkpoint itself is to be rejected
1352
+ // TODO(palla/mbps): Is this ok? Should we be considering a block from a checkpoint that was equivocated?
1353
+ const processBlockFn = () =>
1354
+ processBlock && checkpoint && checkpoint.getBlockProposal()
1355
+ ? this.processValidBlockProposal(checkpoint.getBlockProposal()!, source)
1356
+ : Promise.resolve();
1357
+
1358
+ // A node that skips checkpoint validation attests without re-executing the embedded last block, so run
1359
+ // the checkpoint callback first: this creates and broadcasts the attestation before the block is
1360
+ // processed. Otherwise the block's re-execution — which can stall until the re-execution deadline
1361
+ // waiting for a parent that may never arrive — would delay the attestation past the slot's attestation
1362
+ // window, after which peers reject it as stale.
1363
+ if (this.config.skipCheckpointProposalValidation) {
1364
+ await processCheckpointFn();
1365
+ await processBlockFn();
1366
+ return;
866
1367
  }
867
1368
 
868
- // Mark the txs in this proposal as non-evictable
869
- await this.mempools.txPool.markTxsAsNonEvictable(block.txHashes);
870
- await this.mempools.attestationPool?.addBlockProposal(block);
871
- const attestations = await this.blockReceivedCallback(block, sender);
1369
+ // Process the block first, since it's required for the checkpoint proposal validation.
1370
+ await processBlockFn();
1371
+ await processCheckpointFn();
1372
+ }
872
1373
 
873
- // TODO: fix up this pattern - the abstraction is not nice
874
- // The attestation can be undefined if no handler is registered / the validator deems the block invalid
875
- if (attestations?.length) {
876
- for (const attestation of attestations) {
877
- this.logger.verbose(`Broadcasting attestation for slot ${attestation.slotNumber.toNumber()}`, {
878
- p2pMessageIdentifier: await attestation.p2pMessageIdentifier(),
879
- slot: attestation.slotNumber.toNumber(),
880
- archive: attestation.archive.toString(),
1374
+ /**
1375
+ * Validates a checkpoint proposal. Penalizes peer if validation fails. Adds the checkpoint and
1376
+ * its last block (if present) to the mempool if valid. Triggers equivocation detection on both.
1377
+ */
1378
+ @trackSpan('Libp2pService.validateAndStoreCheckpointProposal', (_peerId, checkpoint) => ({
1379
+ [Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
1380
+ }))
1381
+ protected async validateAndStoreCheckpointProposal(
1382
+ peerId: PeerId,
1383
+ checkpoint: CheckpointProposal,
1384
+ ): Promise<ReceivedMessageValidationResult<CheckpointProposal, { isEquivocated: boolean; processBlock: boolean }>> {
1385
+ const validationResult = await this.checkpointProposalValidator.validate(checkpoint);
1386
+
1387
+ if (validationResult.result === 'reject') {
1388
+ this.logger.warn(`Penalizing peer ${peerId} for checkpoint proposal validation failure`);
1389
+ return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
1390
+ }
1391
+
1392
+ if (validationResult.result === 'ignore') {
1393
+ return { result: TopicValidatorResult.Ignore, obj: checkpoint };
1394
+ }
1395
+
1396
+ // Extract and try to add the block proposal first if present
1397
+ const blockProposal = checkpoint.getBlockProposal();
1398
+ let processBlock = false;
1399
+ if (blockProposal) {
1400
+ this.logger.debug(`Validating block proposal from propagated checkpoint`, {
1401
+ [Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
1402
+ [Attributes.P2P_ID]: peerId.toString(),
1403
+ });
1404
+ const blockProposalResult = await this.validateAndStoreBlockProposal(peerId, blockProposal);
1405
+ const { obj, metadata: { isEquivocated } = {} } = blockProposalResult;
1406
+ if (blockProposalResult.result === TopicValidatorResult.Reject || !obj || isEquivocated) {
1407
+ this.logger.debug(`Rejecting checkpoint due to invalid last block proposal`, {
1408
+ [Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
1409
+ [Attributes.P2P_ID]: peerId.toString(),
1410
+ isEquivocated,
1411
+ result: blockProposalResult.result,
881
1412
  });
882
- await this.broadcastAttestation(attestation);
1413
+ return {
1414
+ result: TopicValidatorResult.Reject,
1415
+ severity:
1416
+ 'severity' in blockProposalResult ? blockProposalResult.severity : PeerErrorSeverity.MidToleranceError,
1417
+ };
1418
+ } else if (blockProposalResult.result === TopicValidatorResult.Accept && obj && !isEquivocated) {
1419
+ processBlock = true;
883
1420
  }
884
1421
  }
1422
+
1423
+ // Try to add the checkpoint proposal core: this handles existence check, cap check, and adding in one call
1424
+ const checkpointCore = checkpoint.toCore();
1425
+ const tryAddResult = await this.mempools.attestationPool.tryAddCheckpointProposal(checkpointCore);
1426
+ const { added, alreadyExists, count } = tryAddResult;
1427
+ const isEquivocated = count !== undefined && count > 1;
1428
+
1429
+ // Duplicate proposal received, do not re-broadcast
1430
+ if (alreadyExists) {
1431
+ this.logger.debug(`Ignoring duplicate checkpoint proposal received`, {
1432
+ ...checkpoint.toCheckpointInfo(),
1433
+ source: peerId.toString(),
1434
+ });
1435
+ return {
1436
+ result: TopicValidatorResult.Ignore,
1437
+ obj: checkpoint,
1438
+ metadata: { isEquivocated, processBlock },
1439
+ };
1440
+ }
1441
+
1442
+ // Too many checkpoint proposals received for this slot, penalize peer and do not re-broadcast
1443
+ // Note: We still return the checkpoint obj so the lastBlock can be processed if valid
1444
+ if (!added) {
1445
+ this.logger.warn(`Penalizing peer for checkpoint proposal exceeding per-slot cap`, {
1446
+ ...checkpoint.toCheckpointInfo(),
1447
+ count,
1448
+ source: peerId.toString(),
1449
+ });
1450
+ return {
1451
+ result: TopicValidatorResult.Reject,
1452
+ obj: checkpoint,
1453
+ metadata: { isEquivocated, processBlock },
1454
+ severity: PeerErrorSeverity.HighToleranceError,
1455
+ };
1456
+ }
1457
+
1458
+ // If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
1459
+ // and do re-broadcast it so other nodes in the network know to slash the proposer
1460
+ if (isEquivocated) {
1461
+ const proposer = checkpoint.getSender();
1462
+ this.logger.warn(`Detected duplicate checkpoint proposal (equivocation) at slot ${checkpoint.slotNumber}`, {
1463
+ ...checkpoint.toCheckpointInfo(),
1464
+ source: peerId.toString(),
1465
+ proposer: proposer?.toString(),
1466
+ });
1467
+ // Invoke the duplicate callback on the first duplicate spotted only
1468
+ if (proposer && count === 2) {
1469
+ this.duplicateProposalCallback?.({ slot: checkpoint.slotNumber, proposer, type: 'checkpoint' });
1470
+ }
1471
+ return {
1472
+ result: TopicValidatorResult.Accept,
1473
+ obj: checkpoint,
1474
+ metadata: { isEquivocated, processBlock },
1475
+ };
1476
+ }
1477
+
1478
+ // Otherwise, we're good to go!
1479
+ return { result: TopicValidatorResult.Accept, obj: checkpoint, metadata: { processBlock, isEquivocated } };
885
1480
  }
886
1481
 
887
1482
  /**
888
- * Broadcast an attestation to all peers.
889
- * @param attestation - The attestation to broadcast.
1483
+ * Process a validated checkpoint proposal.
1484
+ * Note: The proposal was already added to the pool by tryAddCheckpointProposal in handleGossipedCheckpointProposal.
890
1485
  */
891
- @trackSpan('Libp2pService.broadcastAttestation', async attestation => ({
892
- [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toNumber(),
893
- [Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
894
- [Attributes.P2P_ID]: await attestation.p2pMessageIdentifier().then(i => i.toString()),
1486
+ @trackSpan('Libp2pService.processValidCheckpointProposal', async checkpoint => ({
1487
+ [Attributes.SLOT_NUMBER]: checkpoint.slotNumber,
1488
+ [Attributes.BLOCK_ARCHIVE]: checkpoint.archive.toString(),
1489
+ [Attributes.P2P_ID]: await checkpoint.p2pMessageLoggingIdentifier().then(i => i.toString()),
895
1490
  }))
896
- private async broadcastAttestation(attestation: BlockAttestation) {
897
- await this.propagate(attestation);
1491
+ protected async processValidCheckpointProposal(checkpoint: CheckpointProposalCore, sender: PeerId) {
1492
+ const slot = checkpoint.slotNumber;
1493
+ this.logger.verbose(`Received checkpoint proposal for slot ${slot} from external peer ${sender.toString()}.`, {
1494
+ p2pMessageIdentifier: await checkpoint.p2pMessageLoggingIdentifier(),
1495
+ slot: checkpoint.slotNumber,
1496
+ archive: checkpoint.archive.toString(),
1497
+ source: sender.toString(),
1498
+ });
1499
+
1500
+ await this.allNodesCheckpointReceivedCallback(checkpoint, sender);
1501
+
1502
+ // Call the checkpoint received callback with the core version (without lastBlock)
1503
+ // to validate and potentially generate attestations
1504
+ const attestations = await this.validatorCheckpointReceivedCallback(checkpoint, sender);
1505
+ if (attestations && attestations.length > 0) {
1506
+ // If the callback returned attestations, add them to the pool and propagate them
1507
+ await this.mempools.attestationPool.addOwnCheckpointAttestations(attestations);
1508
+ for (const attestation of attestations) {
1509
+ await this.propagate(attestation);
1510
+ }
1511
+ }
898
1512
  }
899
1513
 
900
1514
  /**
@@ -902,212 +1516,186 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
902
1516
  * @param message - The message to propagate.
903
1517
  */
904
1518
  public async propagate<T extends Gossipable>(message: T) {
905
- const p2pMessageIdentifier = await message.p2pMessageIdentifier();
1519
+ const p2pMessageIdentifier = await message.p2pMessageLoggingIdentifier();
906
1520
  this.logger.trace(`Message ${p2pMessageIdentifier} queued`, { p2pMessageIdentifier });
907
- void this.jobQueue
908
- .put(async () => {
909
- await this.sendToPeers(message);
910
- })
911
- .catch(error => {
912
- this.logger.error(`Error propagating message ${p2pMessageIdentifier}`, { error });
913
- });
1521
+ void this.sendToPeers(message).catch(error => {
1522
+ this.logger.error(`Error propagating message ${p2pMessageIdentifier}`, { error });
1523
+ });
914
1524
  }
915
1525
 
916
1526
  /**
917
- * Validate the requested block transactions.
1527
+ * Validate the requested block transactions request-response consistency.
1528
+ * It does NOT validate the transactions themselves.
918
1529
  * @param request - The block transactions request.
919
1530
  * @param response - The block transactions response.
920
1531
  * @param peerId - The ID of the peer that made the request.
921
- * @returns True if the requested block transactions are valid, false otherwise.
1532
+ * @returns True if the request-response is consistent, false otherwise.
922
1533
  */
923
- @trackSpan('Libp2pService.validateRequestedBlockTxs', request => ({
924
- [Attributes.BLOCK_HASH]: request.blockHash.toString(),
1534
+ @trackSpan('Libp2pService.validateRequestedBlockTxsConsistency', request => ({
1535
+ [Attributes.BLOCK_ARCHIVE]: request.archiveRoot.toString(),
925
1536
  }))
926
- private async validateRequestedBlockTxs(
927
- _request: BlockTxsRequest,
1537
+ protected async validateRequestedBlockTxsConsistency(
1538
+ request: BlockTxsRequest,
928
1539
  response: BlockTxsResponse,
929
1540
  peerId: PeerId,
930
1541
  ): Promise<boolean> {
931
- const requestedTxValidator = this.createRequestedTxValidator();
932
-
933
1542
  try {
934
- // TODO(palla/txs): Validate that this tx belongs to the block hash being requested
935
- await Promise.all(response.txs.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator)));
936
- return true;
937
- } catch (e: any) {
938
- if (e instanceof ValidationError) {
939
- this.logger.warn(`Failed validation for requested block txs from peer ${peerId.toString()}`);
940
- } else {
941
- this.logger.error(`Error during validation of requested block txs`, e);
1543
+ // Check for duplicates txs (or hashes) in the response.
1544
+ const returnedHashes = response.txs.map(tx => tx.getTxHash().toString());
1545
+ const uniqueReturned = new Set(returnedHashes.map(h => h.toString()));
1546
+ if (uniqueReturned.size !== returnedHashes.length) {
1547
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1548
+ throw new ValidationError(`Received duplicate txs in block txs response`);
942
1549
  }
943
1550
 
944
- return false;
945
- }
946
- }
947
-
948
- /**
949
- * Validate a collection of txs that has been requested from a peer.
950
- *
951
- * The core component of this validator is that each tx hash MUST match the requested tx hash,
952
- * In order to perform this check, the tx proof must be verified.
953
- *
954
- * Note: This function is called from within `ReqResp.sendRequest` as part of the
955
- * ReqRespSubProtocol.TX subprotocol validation.
956
- *
957
- * @param requestedTxHash - The collection of the txs that was requested.
958
- * @param responseTx - The collectin of txs that was received as a response to the request.
959
- * @param peerId - The peer ID of the peer that sent the tx.
960
- * @returns True if the whole collection of txs is valid, false otherwise.
961
- */
962
- @trackSpan('Libp2pService.validateRequestedTx', (requestedTxHash, _responseTx) => ({
963
- [Attributes.TX_HASH]: requestedTxHash.toString(),
964
- }))
965
- private async validateRequestedTxs(requestedTxHash: TxHash[], responseTx: Tx[], peerId: PeerId): Promise<boolean> {
966
- const requested = new Set(requestedTxHash.map(h => h.toString()));
967
- const requestedTxValidator = this.createRequestedTxValidator();
968
-
969
- //TODO: (mralj) - this is somewhat naive implementation, if single tx is invlid we consider the whole response invalid.
970
- // I think we should still extract the valid txs and return them, so that we can still use the response.
971
- try {
972
- await Promise.all(responseTx.map(tx => this.validateRequestedTx(tx, peerId, requestedTxValidator, requested)));
973
- return true;
974
- } catch (e: any) {
975
- if (e instanceof ValidationError) {
976
- this.logger.warn(`Failed to validate requested txs from peer ${peerId.toString()}, reason ${e.message}`);
977
- } else {
978
- this.logger.error(`Error during validation of requested txs`, e);
1551
+ // We get the block tx hashes from the proposal or the archiver.
1552
+ const proposal = await this.mempools.attestationPool.getBlockProposalByArchive(request.archiveRoot.toString());
1553
+ const blockTxHashes =
1554
+ proposal?.txHashes ??
1555
+ (await this.archiver.getBlock({ archive: request.archiveRoot }))?.body.txEffects.map(e => e.txHash);
1556
+
1557
+ // If we don't have the block, we can't verify membership/order of the returned txs.
1558
+ if (!blockTxHashes) {
1559
+ // This shouldn't happen, since we asked for the block (proposal).
1560
+ // Neither a local proposal nor an archived block: we cannot verify membership/order of the
1561
+ // returned txs. This is a local-state gap, not a peer fault, so we do not penalize.
1562
+ this.logger.warn(
1563
+ `Block ${request.archiveRoot.toString()} not found in attestation pool or archiver; cannot validate membership/order of returned txs`,
1564
+ );
1565
+ // NOTE: We mark the response as invalid, given the limitations of a true/false return value.
1566
+ return false;
979
1567
  }
980
1568
 
981
- return false;
982
- }
983
- }
984
-
985
- private createRequestedTxValidator(): TxValidator {
986
- return new AggregateTxValidator(
987
- new DataTxValidator(),
988
- new MetadataTxValidator({
989
- l1ChainId: new Fr(this.config.l1ChainId),
990
- rollupVersion: new Fr(this.config.rollupVersion),
991
- protocolContractsHash,
992
- vkTreeRoot: getVKTreeRoot(),
993
- }),
994
- new TxProofValidator(this.proofVerifier),
995
- );
996
- }
997
-
998
- private async validateRequestedTx(tx: Tx, peerId: PeerId, txValidator: TxValidator, requested?: Set<`0x${string}`>) {
999
- if (!(await tx.validateTxHash())) {
1000
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1001
- throw new ValidationError(`Received tx with invalid hash ${tx.getTxHash().toString()}.`);
1002
- }
1003
-
1004
- if (requested && !requested.has(tx.getTxHash().toString())) {
1005
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1006
- throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that was not requested.`);
1007
- }
1008
-
1009
- const { result } = await txValidator.validateTx(tx);
1010
- if (result === 'invalid') {
1011
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1012
- throw new ValidationError(`Received tx with hash ${tx.getTxHash().toString()} that is invalid.`);
1013
- }
1014
- }
1015
-
1016
- @trackSpan('Libp2pService.validatePropagatedTx', tx => ({
1017
- [Attributes.TX_HASH]: tx.getTxHash().toString(),
1018
- }))
1019
- private async validatePropagatedTx(tx: Tx, peerId: PeerId): Promise<boolean> {
1020
- const currentBlockNumber = await this.archiver.getBlockNumber();
1569
+ // Verify that the returned tx hashes are a subset of (tx hashes by index) U (explicitly requested tx hashes).
1570
+ const uniqueRequestedHashes = new Set([
1571
+ // tx hashes requested by index.
1572
+ ...request.txIndices.getTrueIndices().map(i => blockTxHashes[i].toString()),
1573
+ // explicitly requested tx hashes.
1574
+ ...request.txHashes.map(h => h.toString()),
1575
+ ]);
1576
+ if (!returnedHashes.every(h => uniqueRequestedHashes.has(h))) {
1577
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1578
+ throw new ValidationError(
1579
+ 'Returned txs should be a subset of (tx hashes by index) U (explicitly requested tx hashes)',
1580
+ );
1581
+ }
1021
1582
 
1022
- // We accept transactions if they are not expired by the next slot (checked based on the IncludeByTimestamp field)
1023
- const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
1024
- const messageValidators = await this.createMessageValidators(currentBlockNumber, nextSlotTimestamp);
1583
+ // Tx indices information is optional, but if present, we need to verify a few things.
1584
+ if (!response.peerHasBlock()) {
1585
+ this.logger.debug(`Peer ${peerId.toString()} signalled missing block`);
1586
+ return true;
1587
+ }
1025
1588
 
1026
- for (const validator of messageValidators) {
1027
- const outcome = await this.runValidations(tx, validator);
1589
+ // If the response has a non-empty bitvector, it needs to be consistent with the number of txs in the block.
1590
+ if (response.txIndices.getLength() !== blockTxHashes.length) {
1591
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError);
1592
+ throw new ValidationError(
1593
+ `Received block txs with mismatched bitvector length: expected ${blockTxHashes.length}, got ${response.txIndices.getLength()}`,
1594
+ );
1595
+ }
1028
1596
 
1029
- if (outcome.allPassed) {
1030
- continue;
1597
+ // For every index we requested that the peer claims to have (per its bitvector), the
1598
+ // corresponding tx must be present in the response. A peer advertising availability but
1599
+ // withholding the tx is inconsistent.
1600
+ const returnedHashSet = new Set(returnedHashes);
1601
+ const expectedIndices = request.txIndices.getTrueIndices().filter(i => response.txIndices.isSet(i));
1602
+ const missingExpected = expectedIndices.some(i => !returnedHashSet.has(blockTxHashes[i].toString()));
1603
+ if (missingExpected) {
1604
+ this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
1605
+ throw new ValidationError('Peer advertised requested txs via indices but did not return them');
1031
1606
  }
1032
- const { name } = outcome.failure;
1033
- let { severity } = outcome.failure;
1034
1607
 
1035
- // Double spend validator has a special case handler
1036
- if (name === 'doubleSpendValidator') {
1037
- const txBlockNumber = currentBlockNumber + 1; // tx is expected to be in the next block
1038
- severity = await this.handleDoubleSpendFailure(tx, txBlockNumber);
1608
+ return true;
1609
+ } catch (e: any) {
1610
+ if (e instanceof ValidationError) {
1611
+ this.logger.warn(`Failed validation for requested block txs from peer ${peerId.toString()}`);
1612
+ } else {
1613
+ this.logger.error(`Error during validation of requested block txs`, e);
1039
1614
  }
1040
1615
 
1041
- this.peerManager.penalizePeer(peerId, severity);
1042
1616
  return false;
1043
1617
  }
1044
- return true;
1045
1618
  }
1046
1619
 
1047
- private async getGasFees(blockNumber: number): Promise<GasFees> {
1048
- if (blockNumber === this.feesCache?.blockNumber) {
1049
- return this.feesCache.gasFees;
1050
- }
1051
-
1052
- const header = await this.archiver.getBlockHeader(blockNumber);
1053
- const gasFees = header?.globalVariables.gasFees ?? GasFees.empty();
1054
- this.feesCache = { blockNumber, gasFees };
1055
- return gasFees;
1620
+ private getGasFees(): Promise<GasFees> {
1621
+ return this.blockMinFeesProvider.getCurrentMinFees();
1056
1622
  }
1057
1623
 
1058
- public async validate(txs: Tx[]): Promise<void> {
1059
- const currentBlockNumber = await this.archiver.getBlockNumber();
1624
+ /**
1625
+ * Get the BatchTxRequesterLibP2PService dependencies for creating BatchTxRequester instances
1626
+ */
1627
+ public getBatchTxRequesterService(): BatchTxRequesterLibP2PService {
1628
+ return {
1629
+ reqResp: this.reqresp,
1630
+ connectionSampler: this.reqresp.getConnectionSampler(),
1631
+ txValidatorConfig: {
1632
+ l1ChainId: this.config.l1ChainId,
1633
+ rollupVersion: this.config.rollupVersion,
1634
+ proofVerifier: this.proofVerifier,
1635
+ txValidationCache: this.txValidationCache,
1636
+ },
1637
+ peerScoring: this.peerManager,
1638
+ validateRequestedBlockTxsConsistency: this.validateRequestedBlockTxsConsistency.bind(this),
1639
+ };
1640
+ }
1060
1641
 
1061
- // We accept transactions if they are not expired by the next slot (checked based on the IncludeByTimestamp field)
1062
- const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
1063
- const messageValidators = await this.createMessageValidators(currentBlockNumber, nextSlotTimestamp);
1642
+ public async validateTxsReceivedInBlockProposal(txs: Tx[]): Promise<void> {
1643
+ const validator = createTxValidatorForBlockProposalReceivedTxs(
1644
+ this.proofVerifier,
1645
+ { l1ChainId: this.config.l1ChainId, rollupVersion: this.config.rollupVersion },
1646
+ this.logger.getBindings(),
1647
+ this.txValidationCache,
1648
+ );
1064
1649
 
1065
- await Promise.all(
1650
+ const results = await Promise.all(
1066
1651
  txs.map(async tx => {
1067
- for (const validator of messageValidators) {
1068
- const outcome = await this.runValidations(tx, validator);
1069
- if (!outcome.allPassed) {
1070
- throw new Error('Invalid tx detected', { cause: { outcome } });
1071
- }
1072
- }
1652
+ const result = await validator.validateTx(tx);
1653
+ return result.result !== 'invalid';
1073
1654
  }),
1074
1655
  );
1656
+ if (results.some(value => value === false)) {
1657
+ throw new Error('Invalid tx detected');
1658
+ }
1075
1659
  }
1076
1660
 
1077
- /**
1078
- * Create message validators for the given block number and timestamp.
1079
- *
1080
- * Each validator is a pair of a validator and a severity.
1081
- * If a validator fails, the peer is penalized with the severity of the validator.
1082
- *
1083
- * @param currentBlockNumber - The current synced block number.
1084
- * @param nextSlotTimestamp - The timestamp of the next slot (used to validate txs are not expired).
1085
- * @returns The message validators.
1086
- */
1087
- private async createMessageValidators(
1088
- currentBlockNumber: number,
1661
+ /** Creates the first stage (fast) validators for gossiped transactions. */
1662
+ protected async createFirstStageMessageValidators(
1663
+ currentBlockNumber: BlockNumber,
1089
1664
  nextSlotTimestamp: UInt64,
1090
- ): Promise<Record<string, MessageValidator>[]> {
1091
- const gasFees = await this.getGasFees(currentBlockNumber);
1092
- const allowedInSetup = this.config.txPublicSetupAllowList ?? (await getDefaultAllowedSetupFunctions());
1093
-
1094
- const blockNumberInWhichTheTxIsConsideredToBeIncluded = currentBlockNumber + 1;
1095
-
1096
- return createTxMessageValidators(
1665
+ ): Promise<Record<string, TransactionValidator>> {
1666
+ const gasFees = await this.getGasFees();
1667
+ const allowedInSetup = [
1668
+ ...(await getDefaultAllowedSetupFunctions()),
1669
+ ...(this.config.txPublicSetupAllowListExtend ?? []),
1670
+ ];
1671
+ const blockNumber = BlockNumber(currentBlockNumber + 1);
1672
+ const l1Constants = await this.archiver.getL1Constants();
1673
+
1674
+ return createFirstStageTxValidationsForGossipedTransactions(
1097
1675
  nextSlotTimestamp,
1098
- blockNumberInWhichTheTxIsConsideredToBeIncluded,
1676
+ blockNumber,
1099
1677
  this.worldStateSynchronizer,
1100
1678
  gasFees,
1101
1679
  this.config.l1ChainId,
1102
1680
  this.config.rollupVersion,
1103
1681
  protocolContractsHash,
1104
1682
  this.archiver,
1105
- this.proofVerifier,
1106
1683
  !this.config.disableTransactions,
1107
1684
  allowedInSetup,
1685
+ this.logger.getBindings(),
1686
+ {
1687
+ rollupManaLimit: l1Constants.rollupManaLimit,
1688
+ maxBlockL2Gas: this.config.validateMaxL2BlockGas,
1689
+ maxBlockDAGas: this.config.validateMaxDABlockGas,
1690
+ },
1108
1691
  );
1109
1692
  }
1110
1693
 
1694
+ /** Creates the second stage (expensive proof verification) validators for gossiped transactions. */
1695
+ protected createSecondStageMessageValidators(): Record<string, TransactionValidator> {
1696
+ return createSecondStageTxValidationsForGossipedTransactions(this.proofVerifier, this.logger.getBindings());
1697
+ }
1698
+
1111
1699
  /**
1112
1700
  * Run validations on a tx.
1113
1701
  * @param tx - The tx to validate.
@@ -1116,7 +1704,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1116
1704
  */
1117
1705
  private async runValidations(
1118
1706
  tx: Tx,
1119
- messageValidators: Record<string, MessageValidator>,
1707
+ messageValidators: Record<string, TransactionValidator>,
1120
1708
  ): Promise<ValidationOutcome> {
1121
1709
  const validationPromises = Object.entries(messageValidators).map(async ([name, { validator, severity }]) => {
1122
1710
  const { result } = await validator.validateTx(tx);
@@ -1125,8 +1713,10 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1125
1713
 
1126
1714
  // A promise that resolves when all validations have been run
1127
1715
  const allValidations = await Promise.all(validationPromises);
1128
- const failed = allValidations.find(x => !x.isValid);
1129
- if (failed) {
1716
+ const failures = allValidations.filter(x => !x.isValid);
1717
+ if (failures.length > 0) {
1718
+ // Pick the most severe failure (lowest tolerance = harshest penalty)
1719
+ const failed = maxBy(failures, f => PeerErrorSeverityByHarshness.indexOf(f.severity))!;
1130
1720
  return {
1131
1721
  allPassed: false,
1132
1722
  failure: {
@@ -1153,20 +1743,23 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1153
1743
  * @param peerId - The peer ID of the peer that sent the tx.
1154
1744
  * @returns Severity
1155
1745
  */
1156
- private async handleDoubleSpendFailure(tx: Tx, blockNumber: number): Promise<PeerErrorSeverity> {
1746
+ private async handleDoubleSpendFailure(tx: Tx, blockNumber: BlockNumber): Promise<PeerErrorSeverity> {
1157
1747
  if (blockNumber <= this.config.doubleSpendSeverePeerPenaltyWindow) {
1158
1748
  return PeerErrorSeverity.HighToleranceError;
1159
1749
  }
1160
1750
 
1161
- const snapshotValidator = new DoubleSpendTxValidator({
1162
- nullifiersExist: async (nullifiers: Buffer[]) => {
1163
- const merkleTree = this.worldStateSynchronizer.getSnapshot(
1164
- blockNumber - this.config.doubleSpendSeverePeerPenaltyWindow,
1165
- );
1166
- const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
1167
- return indices.map(index => index !== undefined);
1751
+ const snapshotValidator = new DoubleSpendTxValidator(
1752
+ {
1753
+ nullifiersExist: async (nullifiers: Buffer[]) => {
1754
+ const merkleTree = this.worldStateSynchronizer.getSnapshot(
1755
+ BlockNumber(blockNumber - this.config.doubleSpendSeverePeerPenaltyWindow),
1756
+ );
1757
+ const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
1758
+ return indices.map(index => index !== undefined);
1759
+ },
1168
1760
  },
1169
- });
1761
+ this.logger.getBindings(),
1762
+ );
1170
1763
 
1171
1764
  const validSnapshot = await snapshotValidator.validateTx(tx);
1172
1765
  if (validSnapshot.result !== 'valid') {
@@ -1176,47 +1769,6 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1176
1769
  return PeerErrorSeverity.HighToleranceError;
1177
1770
  }
1178
1771
 
1179
- /**
1180
- * Validate an attestation.
1181
- *
1182
- * @param attestation - The attestation to validate.
1183
- * @returns True if the attestation is valid, false otherwise.
1184
- */
1185
- @trackSpan('Libp2pService.validateAttestation', async (_, attestation) => ({
1186
- [Attributes.SLOT_NUMBER]: attestation.payload.header.slotNumber.toNumber(),
1187
- [Attributes.BLOCK_ARCHIVE]: attestation.archive.toString(),
1188
- [Attributes.P2P_ID]: await attestation.p2pMessageIdentifier().then(i => i.toString()),
1189
- }))
1190
- public async validateAttestation(peerId: PeerId, attestation: BlockAttestation): Promise<boolean> {
1191
- const severity = await this.attestationValidator.validate(attestation);
1192
- if (severity) {
1193
- this.peerManager.penalizePeer(peerId, severity);
1194
- return false;
1195
- }
1196
-
1197
- return true;
1198
- }
1199
-
1200
- /**
1201
- * Validate a block proposal.
1202
- *
1203
- * @param block - The block proposal to validate.
1204
- * @returns True if the block proposal is valid, false otherwise.
1205
- */
1206
- @trackSpan('Libp2pService.validateBlockProposal', (_peerId, block) => ({
1207
- [Attributes.SLOT_NUMBER]: block.payload.header.slotNumber.toString(),
1208
- }))
1209
- public async validateBlockProposal(peerId: PeerId, block: BlockProposal): Promise<boolean> {
1210
- const severity = await this.blockProposalValidator.validate(block);
1211
- if (severity) {
1212
- this.logger.debug(`Penalizing peer ${peerId} for block proposal validation failure`);
1213
- this.peerManager.penalizePeer(peerId, severity);
1214
- return false;
1215
- }
1216
-
1217
- return true;
1218
- }
1219
-
1220
1772
  public getPeerScore(peerId: PeerId): number {
1221
1773
  return this.node.services.pubsub.score.score(peerId.toString());
1222
1774
  }
@@ -1228,7 +1780,7 @@ export class LibP2PService<T extends P2PClientType = P2PClientType.Full> extends
1228
1780
  private async sendToPeers<T extends Gossipable>(message: T) {
1229
1781
  const parent = message.constructor as typeof Gossipable;
1230
1782
 
1231
- const identifier = await message.p2pMessageIdentifier().then(i => i.toString());
1783
+ const identifier = await message.p2pMessageLoggingIdentifier().then(i => i.toString());
1232
1784
  this.logger.trace(`Sending message ${identifier}`, { p2pMessageIdentifier: identifier });
1233
1785
 
1234
1786
  const recipientsNum = await this.publishToTopic(this.topicStrings[parent.p2pTopic], message);