@aztec/p2p 0.0.1-commit.f2ce05ee → 0.0.1-commit.f5a9928

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 (522) hide show
  1. package/README.md +130 -3
  2. package/dest/bootstrap/bootstrap.d.ts +1 -1
  3. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  4. package/dest/bootstrap/bootstrap.js +22 -6
  5. package/dest/client/factory.d.ts +11 -10
  6. package/dest/client/factory.d.ts.map +1 -1
  7. package/dest/client/factory.js +64 -17
  8. package/dest/client/interface.d.ts +86 -40
  9. package/dest/client/interface.d.ts.map +1 -1
  10. package/dest/client/p2p_client.d.ts +67 -64
  11. package/dest/client/p2p_client.d.ts.map +1 -1
  12. package/dest/client/p2p_client.js +251 -251
  13. package/dest/config.d.ts +173 -94
  14. package/dest/config.d.ts.map +1 -1
  15. package/dest/config.js +158 -44
  16. package/dest/errors/p2p-service.error.d.ts +9 -0
  17. package/dest/errors/p2p-service.error.d.ts.map +1 -0
  18. package/dest/errors/p2p-service.error.js +10 -0
  19. package/dest/errors/reqresp.error.d.ts +9 -16
  20. package/dest/errors/reqresp.error.d.ts.map +1 -1
  21. package/dest/errors/reqresp.error.js +10 -17
  22. package/dest/errors/tx-pool.error.d.ts +8 -0
  23. package/dest/errors/tx-pool.error.d.ts.map +1 -0
  24. package/dest/errors/tx-pool.error.js +9 -0
  25. package/dest/index.d.ts +2 -2
  26. package/dest/index.d.ts.map +1 -1
  27. package/dest/index.js +1 -1
  28. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +106 -55
  29. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  30. package/dest/mem_pools/attestation_pool/attestation_pool.js +302 -195
  31. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +1 -1
  32. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  33. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +274 -115
  34. package/dest/mem_pools/attestation_pool/index.d.ts +2 -2
  35. package/dest/mem_pools/attestation_pool/index.d.ts.map +1 -1
  36. package/dest/mem_pools/attestation_pool/index.js +1 -1
  37. package/dest/mem_pools/attestation_pool/mocks.d.ts +2 -2
  38. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  39. package/dest/mem_pools/attestation_pool/mocks.js +7 -5
  40. package/dest/mem_pools/index.d.ts +2 -2
  41. package/dest/mem_pools/index.d.ts.map +1 -1
  42. package/dest/mem_pools/instrumentation.d.ts +12 -5
  43. package/dest/mem_pools/instrumentation.d.ts.map +1 -1
  44. package/dest/mem_pools/instrumentation.js +38 -17
  45. package/dest/mem_pools/interface.d.ts +3 -3
  46. package/dest/mem_pools/interface.d.ts.map +1 -1
  47. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts +10 -5
  48. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts.map +1 -1
  49. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.js +29 -20
  50. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +104 -0
  51. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -0
  52. package/dest/mem_pools/tx_pool_v2/deleted_pool.js +251 -0
  53. package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts +3 -3
  54. package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts.map +1 -1
  55. package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.js +18 -9
  56. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts +1 -1
  57. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -1
  58. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +8 -4
  59. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts +3 -3
  60. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts.map +1 -1
  61. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.js +15 -5
  62. package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts +3 -2
  63. package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts.map +1 -1
  64. package/dest/mem_pools/tx_pool_v2/eviction/index.js +2 -1
  65. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts +16 -0
  66. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts.map +1 -0
  67. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.js +64 -0
  68. package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts +54 -5
  69. package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts.map +1 -1
  70. package/dest/mem_pools/tx_pool_v2/eviction/interfaces.js +8 -0
  71. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts +1 -1
  72. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts.map +1 -1
  73. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.js +6 -5
  74. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts +1 -1
  75. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts.map +1 -1
  76. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +9 -8
  77. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts +2 -2
  78. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts.map +1 -1
  79. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.js +15 -6
  80. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts +4 -4
  81. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts.map +1 -1
  82. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.js +16 -4
  83. package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts +3 -3
  84. package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts.map +1 -1
  85. package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.js +3 -3
  86. package/dest/mem_pools/tx_pool_v2/index.d.ts +3 -2
  87. package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -1
  88. package/dest/mem_pools/tx_pool_v2/index.js +2 -1
  89. package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts +25 -0
  90. package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts.map +1 -0
  91. package/dest/mem_pools/tx_pool_v2/instrumentation.js +67 -0
  92. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +62 -16
  93. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
  94. package/dest/mem_pools/tx_pool_v2/interfaces.js +6 -1
  95. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +81 -15
  96. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
  97. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +147 -19
  98. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +26 -3
  99. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -1
  100. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +79 -43
  101. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +27 -7
  102. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
  103. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +94 -32
  104. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +62 -9
  105. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
  106. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +537 -246
  107. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +10 -3
  108. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
  109. package/dest/msg_validators/attestation_validator/attestation_validator.js +38 -15
  110. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +7 -3
  111. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
  112. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +4 -5
  113. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +12 -5
  114. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
  115. package/dest/msg_validators/proposal_validator/block_proposal_validator.js +11 -3
  116. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +12 -5
  117. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
  118. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +23 -3
  119. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +36 -9
  120. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
  121. package/dest/msg_validators/proposal_validator/proposal_validator.js +130 -48
  122. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +4 -4
  123. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
  124. package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +11 -18
  125. package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts +2 -1
  126. package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts.map +1 -1
  127. package/dest/msg_validators/tx_validator/allowed_public_setup.js +25 -21
  128. package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts +17 -0
  129. package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts.map +1 -0
  130. package/dest/msg_validators/tx_validator/allowed_setup_helpers.js +24 -0
  131. package/dest/msg_validators/tx_validator/archive_cache.js +1 -1
  132. package/dest/msg_validators/tx_validator/block_header_validator.d.ts +16 -3
  133. package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
  134. package/dest/msg_validators/tx_validator/block_header_validator.js +1 -1
  135. package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts +15 -0
  136. package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts.map +1 -0
  137. package/dest/msg_validators/tx_validator/cached_tx_validator.js +19 -0
  138. package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts +9 -0
  139. package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts.map +1 -0
  140. package/dest/msg_validators/tx_validator/contract_instance_validator.js +48 -0
  141. package/dest/msg_validators/tx_validator/data_validator.d.ts +2 -1
  142. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
  143. package/dest/msg_validators/tx_validator/data_validator.js +36 -2
  144. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +13 -3
  145. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
  146. package/dest/msg_validators/tx_validator/double_spend_validator.js +4 -4
  147. package/dest/msg_validators/tx_validator/factory.d.ts +133 -7
  148. package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
  149. package/dest/msg_validators/tx_validator/factory.js +261 -61
  150. package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts +1 -1
  151. package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts.map +1 -1
  152. package/dest/msg_validators/tx_validator/fee_payer_balance.js +6 -2
  153. package/dest/msg_validators/tx_validator/gas_validator.d.ts +104 -3
  154. package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
  155. package/dest/msg_validators/tx_validator/gas_validator.js +137 -53
  156. package/dest/msg_validators/tx_validator/index.d.ts +5 -1
  157. package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
  158. package/dest/msg_validators/tx_validator/index.js +4 -0
  159. package/dest/msg_validators/tx_validator/metadata_validator.d.ts +1 -1
  160. package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
  161. package/dest/msg_validators/tx_validator/metadata_validator.js +4 -4
  162. package/dest/msg_validators/tx_validator/nullifier_cache.d.ts +14 -0
  163. package/dest/msg_validators/tx_validator/nullifier_cache.d.ts.map +1 -0
  164. package/dest/msg_validators/tx_validator/nullifier_cache.js +24 -0
  165. package/dest/msg_validators/tx_validator/phases_validator.d.ts +22 -2
  166. package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
  167. package/dest/msg_validators/tx_validator/phases_validator.js +72 -24
  168. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +20 -4
  169. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -1
  170. package/dest/msg_validators/tx_validator/timestamp_validator.js +6 -6
  171. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +2 -1
  172. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -1
  173. package/dest/msg_validators/tx_validator/tx_proof_validator.js +2 -0
  174. package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts +48 -0
  175. package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts.map +1 -0
  176. package/dest/msg_validators/tx_validator/tx_validation_cache.js +68 -0
  177. package/dest/services/data_store.d.ts +1 -1
  178. package/dest/services/data_store.d.ts.map +1 -1
  179. package/dest/services/data_store.js +5 -5
  180. package/dest/services/discv5/discV5_service.d.ts +13 -2
  181. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  182. package/dest/services/discv5/discV5_service.js +119 -23
  183. package/dest/services/discv5/persisted_enr_store.d.ts +38 -0
  184. package/dest/services/discv5/persisted_enr_store.d.ts.map +1 -0
  185. package/dest/services/discv5/persisted_enr_store.js +145 -0
  186. package/dest/services/dummy_service.d.ts +25 -19
  187. package/dest/services/dummy_service.d.ts.map +1 -1
  188. package/dest/services/dummy_service.js +28 -20
  189. package/dest/services/encoding.d.ts +24 -7
  190. package/dest/services/encoding.d.ts.map +1 -1
  191. package/dest/services/encoding.js +43 -26
  192. package/dest/services/gossipsub/topic_score_params.d.ts +23 -17
  193. package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -1
  194. package/dest/services/gossipsub/topic_score_params.js +36 -24
  195. package/dest/services/libp2p/instrumentation.d.ts +6 -1
  196. package/dest/services/libp2p/instrumentation.d.ts.map +1 -1
  197. package/dest/services/libp2p/instrumentation.js +21 -0
  198. package/dest/services/libp2p/libp2p_service.d.ts +71 -60
  199. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  200. package/dest/services/libp2p/libp2p_service.js +602 -407
  201. package/dest/services/peer-manager/metrics.d.ts +3 -1
  202. package/dest/services/peer-manager/metrics.d.ts.map +1 -1
  203. package/dest/services/peer-manager/metrics.js +6 -0
  204. package/dest/services/peer-manager/peer_manager.d.ts +31 -3
  205. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  206. package/dest/services/peer-manager/peer_manager.js +127 -26
  207. package/dest/services/peer-manager/peer_scoring.d.ts +34 -2
  208. package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
  209. package/dest/services/peer-manager/peer_scoring.js +102 -13
  210. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +5 -8
  211. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
  212. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +95 -127
  213. package/dest/services/reqresp/batch-tx-requester/interface.d.ts +11 -11
  214. package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -1
  215. package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts +11 -13
  216. package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts.map +1 -1
  217. package/dest/services/reqresp/batch-tx-requester/missing_txs.js +31 -46
  218. package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts +19 -11
  219. package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts.map +1 -1
  220. package/dest/services/reqresp/batch-tx-requester/peer_collection.js +52 -15
  221. package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts +5 -14
  222. package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts.map +1 -1
  223. package/dest/services/reqresp/batch-tx-requester/tx_validator.js +6 -20
  224. package/dest/services/reqresp/config.d.ts +15 -3
  225. package/dest/services/reqresp/config.d.ts.map +1 -1
  226. package/dest/services/reqresp/config.js +11 -0
  227. package/dest/services/reqresp/interface.d.ts +25 -18
  228. package/dest/services/reqresp/interface.d.ts.map +1 -1
  229. package/dest/services/reqresp/interface.js +23 -19
  230. package/dest/services/reqresp/metrics.d.ts +1 -1
  231. package/dest/services/reqresp/metrics.d.ts.map +1 -1
  232. package/dest/services/reqresp/metrics.js +0 -1
  233. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +4 -3
  234. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -1
  235. package/dest/services/reqresp/protocols/block_txs/bitvector.js +3 -2
  236. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +3 -3
  237. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
  238. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +31 -28
  239. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +18 -7
  240. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
  241. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +57 -24
  242. package/dest/services/reqresp/protocols/index.d.ts +1 -2
  243. package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
  244. package/dest/services/reqresp/protocols/index.js +0 -1
  245. package/dest/services/reqresp/protocols/tx.d.ts +7 -1
  246. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
  247. package/dest/services/reqresp/protocols/tx.js +21 -3
  248. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +5 -4
  249. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
  250. package/dest/services/reqresp/rate-limiter/rate_limiter.js +10 -8
  251. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
  252. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
  253. package/dest/services/reqresp/rate-limiter/rate_limits.js +3 -13
  254. package/dest/services/reqresp/reqresp.d.ts +7 -29
  255. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  256. package/dest/services/reqresp/reqresp.js +85 -219
  257. package/dest/services/service.d.ts +63 -19
  258. package/dest/services/service.d.ts.map +1 -1
  259. package/dest/services/tx_collection/config.d.ts +11 -14
  260. package/dest/services/tx_collection/config.d.ts.map +1 -1
  261. package/dest/services/tx_collection/config.js +26 -33
  262. package/dest/services/tx_collection/file_store_tx_collection.d.ts +37 -0
  263. package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -0
  264. package/dest/services/tx_collection/file_store_tx_collection.js +127 -0
  265. package/dest/services/tx_collection/file_store_tx_source.d.ts +38 -0
  266. package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -0
  267. package/dest/services/tx_collection/file_store_tx_source.js +100 -0
  268. package/dest/services/tx_collection/index.d.ts +3 -3
  269. package/dest/services/tx_collection/index.d.ts.map +1 -1
  270. package/dest/services/tx_collection/index.js +1 -1
  271. package/dest/services/tx_collection/instrumentation.js +1 -2
  272. package/dest/services/tx_collection/request_tracker.d.ts +53 -0
  273. package/dest/services/tx_collection/request_tracker.d.ts.map +1 -0
  274. package/dest/services/tx_collection/request_tracker.js +84 -0
  275. package/dest/services/tx_collection/tx_collection.d.ts +45 -51
  276. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
  277. package/dest/services/tx_collection/tx_collection.js +296 -68
  278. package/dest/services/tx_collection/tx_collection_sink.d.ts +19 -9
  279. package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
  280. package/dest/services/tx_collection/tx_collection_sink.js +28 -31
  281. package/dest/services/tx_collection/tx_source.d.ts +13 -7
  282. package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
  283. package/dest/services/tx_collection/tx_source.js +29 -7
  284. package/dest/services/tx_file_store/config.d.ts +1 -3
  285. package/dest/services/tx_file_store/config.d.ts.map +1 -1
  286. package/dest/services/tx_file_store/config.js +0 -4
  287. package/dest/services/tx_file_store/tx_file_store.d.ts +4 -5
  288. package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -1
  289. package/dest/services/tx_file_store/tx_file_store.js +13 -20
  290. package/dest/services/tx_provider.d.ts +6 -4
  291. package/dest/services/tx_provider.d.ts.map +1 -1
  292. package/dest/services/tx_provider.js +12 -8
  293. package/dest/test-helpers/make-test-p2p-clients.d.ts +7 -8
  294. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  295. package/dest/test-helpers/make-test-p2p-clients.js +5 -3
  296. package/dest/test-helpers/mock-pubsub.d.ts +56 -6
  297. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  298. package/dest/test-helpers/mock-pubsub.js +128 -14
  299. package/dest/test-helpers/reqresp-nodes.d.ts +5 -7
  300. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  301. package/dest/test-helpers/reqresp-nodes.js +17 -19
  302. package/dest/test-helpers/test_tx_provider.d.ts +3 -1
  303. package/dest/test-helpers/test_tx_provider.d.ts.map +1 -1
  304. package/dest/test-helpers/test_tx_provider.js +3 -0
  305. package/dest/test-helpers/testbench-utils.d.ts +49 -37
  306. package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
  307. package/dest/test-helpers/testbench-utils.js +142 -50
  308. package/dest/testbench/p2p_client_testbench_worker.d.ts +3 -5
  309. package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -1
  310. package/dest/testbench/p2p_client_testbench_worker.js +112 -49
  311. package/dest/testbench/worker_client_manager.d.ts +12 -6
  312. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  313. package/dest/testbench/worker_client_manager.js +57 -11
  314. package/dest/util.d.ts +13 -8
  315. package/dest/util.d.ts.map +1 -1
  316. package/dest/util.js +35 -14
  317. package/dest/versioning.d.ts +3 -6
  318. package/dest/versioning.d.ts.map +1 -1
  319. package/dest/versioning.js +3 -24
  320. package/package.json +17 -17
  321. package/src/bootstrap/bootstrap.ts +17 -3
  322. package/src/client/factory.ts +129 -28
  323. package/src/client/interface.ts +88 -48
  324. package/src/client/p2p_client.ts +284 -317
  325. package/src/client/test/{tx_proposal_collector/README.md → p2p_client.batch_tx_requester.bench.README.md} +23 -53
  326. package/src/config.ts +280 -49
  327. package/src/errors/p2p-service.error.ts +11 -0
  328. package/src/errors/reqresp.error.ts +13 -19
  329. package/src/errors/tx-pool.error.ts +12 -0
  330. package/src/index.ts +1 -1
  331. package/src/mem_pools/attestation_pool/attestation_pool.ts +373 -236
  332. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +298 -119
  333. package/src/mem_pools/attestation_pool/index.ts +3 -3
  334. package/src/mem_pools/attestation_pool/mocks.ts +14 -8
  335. package/src/mem_pools/index.ts +1 -1
  336. package/src/mem_pools/instrumentation.ts +27 -17
  337. package/src/mem_pools/interface.ts +2 -2
  338. package/src/mem_pools/tx_pool_v2/README.md +85 -11
  339. package/src/mem_pools/tx_pool_v2/archive/tx_archive.ts +30 -20
  340. package/src/mem_pools/tx_pool_v2/deleted_pool.ts +321 -0
  341. package/src/mem_pools/tx_pool_v2/eviction/eviction_manager.ts +21 -8
  342. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +6 -4
  343. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +21 -6
  344. package/src/mem_pools/tx_pool_v2/eviction/index.ts +5 -0
  345. package/src/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.ts +67 -0
  346. package/src/mem_pools/tx_pool_v2/eviction/interfaces.ts +59 -4
  347. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.ts +4 -6
  348. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +8 -10
  349. package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +14 -10
  350. package/src/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.ts +33 -6
  351. package/src/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.ts +4 -3
  352. package/src/mem_pools/tx_pool_v2/index.ts +2 -1
  353. package/src/mem_pools/tx_pool_v2/instrumentation.ts +96 -0
  354. package/src/mem_pools/tx_pool_v2/interfaces.ts +63 -16
  355. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +215 -27
  356. package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +91 -43
  357. package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +122 -33
  358. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +602 -242
  359. package/src/msg_validators/attestation_validator/README.md +49 -0
  360. package/src/msg_validators/attestation_validator/attestation_validator.ts +46 -14
  361. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +15 -7
  362. package/src/msg_validators/proposal_validator/README.md +123 -0
  363. package/src/msg_validators/proposal_validator/block_proposal_validator.ts +26 -4
  364. package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +43 -7
  365. package/src/msg_validators/proposal_validator/proposal_validator.ts +155 -49
  366. package/src/msg_validators/tx_validator/README.md +129 -0
  367. package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +8 -17
  368. package/src/msg_validators/tx_validator/allowed_public_setup.ts +22 -27
  369. package/src/msg_validators/tx_validator/allowed_setup_helpers.ts +31 -0
  370. package/src/msg_validators/tx_validator/archive_cache.ts +1 -1
  371. package/src/msg_validators/tx_validator/block_header_validator.ts +15 -3
  372. package/src/msg_validators/tx_validator/cached_tx_validator.ts +31 -0
  373. package/src/msg_validators/tx_validator/contract_instance_validator.ts +56 -0
  374. package/src/msg_validators/tx_validator/data_validator.ts +44 -1
  375. package/src/msg_validators/tx_validator/double_spend_validator.ts +11 -6
  376. package/src/msg_validators/tx_validator/factory.ts +413 -80
  377. package/src/msg_validators/tx_validator/fee_payer_balance.ts +6 -2
  378. package/src/msg_validators/tx_validator/gas_validator.ts +195 -54
  379. package/src/msg_validators/tx_validator/index.ts +4 -0
  380. package/src/msg_validators/tx_validator/metadata_validator.ts +12 -4
  381. package/src/msg_validators/tx_validator/nullifier_cache.ts +30 -0
  382. package/src/msg_validators/tx_validator/phases_validator.ts +82 -27
  383. package/src/msg_validators/tx_validator/timestamp_validator.ts +23 -18
  384. package/src/msg_validators/tx_validator/tx_proof_validator.ts +2 -0
  385. package/src/msg_validators/tx_validator/tx_validation_cache.ts +101 -0
  386. package/src/services/data_store.ts +5 -13
  387. package/src/services/discv5/discV5_service.ts +126 -14
  388. package/src/services/discv5/persisted_enr_store.ts +158 -0
  389. package/src/services/dummy_service.ts +38 -46
  390. package/src/services/encoding.ts +42 -26
  391. package/src/services/gossipsub/README.md +59 -22
  392. package/src/services/gossipsub/topic_score_params.ts +58 -31
  393. package/src/services/libp2p/instrumentation.ts +22 -0
  394. package/src/services/libp2p/libp2p_service.ts +687 -466
  395. package/src/services/peer-manager/metrics.ts +7 -0
  396. package/src/services/peer-manager/peer_manager.ts +140 -25
  397. package/src/services/peer-manager/peer_scoring.ts +102 -8
  398. package/src/services/reqresp/README.md +194 -0
  399. package/src/services/reqresp/batch-tx-requester/README.md +46 -7
  400. package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +92 -138
  401. package/src/services/reqresp/batch-tx-requester/interface.ts +14 -10
  402. package/src/services/reqresp/batch-tx-requester/missing_txs.ts +30 -71
  403. package/src/services/reqresp/batch-tx-requester/peer_collection.ts +68 -24
  404. package/src/services/reqresp/batch-tx-requester/tx_validator.ts +12 -25
  405. package/src/services/reqresp/config.ts +19 -2
  406. package/src/services/reqresp/interface.ts +45 -46
  407. package/src/services/reqresp/metrics.ts +0 -1
  408. package/src/services/reqresp/protocols/block_txs/bitvector.ts +3 -2
  409. package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +36 -32
  410. package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +64 -20
  411. package/src/services/reqresp/protocols/index.ts +0 -1
  412. package/src/services/reqresp/protocols/tx.ts +23 -3
  413. package/src/services/reqresp/rate-limiter/rate_limiter.ts +13 -9
  414. package/src/services/reqresp/rate-limiter/rate_limits.ts +3 -13
  415. package/src/services/reqresp/reqresp.ts +100 -263
  416. package/src/services/service.ts +80 -35
  417. package/src/services/tx_collection/config.ts +40 -49
  418. package/src/services/tx_collection/file_store_tx_collection.ts +153 -0
  419. package/src/services/tx_collection/file_store_tx_source.ts +129 -0
  420. package/src/services/tx_collection/index.ts +2 -6
  421. package/src/services/tx_collection/instrumentation.ts +1 -1
  422. package/src/services/tx_collection/request_tracker.ts +127 -0
  423. package/src/services/tx_collection/tx_collection.ts +362 -110
  424. package/src/services/tx_collection/tx_collection_sink.ts +32 -36
  425. package/src/services/tx_collection/tx_source.ts +29 -8
  426. package/src/services/tx_file_store/config.ts +0 -6
  427. package/src/services/tx_file_store/tx_file_store.ts +15 -25
  428. package/src/services/tx_provider.ts +15 -9
  429. package/src/test-helpers/make-test-p2p-clients.ts +7 -6
  430. package/src/test-helpers/mock-pubsub.ts +152 -14
  431. package/src/test-helpers/reqresp-nodes.ts +15 -28
  432. package/src/test-helpers/test_tx_provider.ts +5 -0
  433. package/src/test-helpers/testbench-utils.ts +171 -68
  434. package/src/testbench/p2p_client_testbench_worker.ts +121 -64
  435. package/src/testbench/worker_client_manager.ts +72 -25
  436. package/src/util.ts +40 -19
  437. package/src/versioning.ts +3 -33
  438. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts +0 -2
  439. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts.map +0 -1
  440. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +0 -305
  441. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts +0 -73
  442. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts.map +0 -1
  443. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.js +0 -8
  444. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +0 -125
  445. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +0 -1
  446. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +0 -596
  447. package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts +0 -32
  448. package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts.map +0 -1
  449. package/dest/mem_pools/tx_pool/eviction/eviction_manager.js +0 -112
  450. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts +0 -157
  451. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts.map +0 -1
  452. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.js +0 -52
  453. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts +0 -16
  454. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts.map +0 -1
  455. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.js +0 -122
  456. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts +0 -17
  457. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts.map +0 -1
  458. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.js +0 -84
  459. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts +0 -19
  460. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts.map +0 -1
  461. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.js +0 -78
  462. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts +0 -26
  463. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts.map +0 -1
  464. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.js +0 -84
  465. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts +0 -25
  466. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts.map +0 -1
  467. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.js +0 -57
  468. package/dest/mem_pools/tx_pool/index.d.ts +0 -3
  469. package/dest/mem_pools/tx_pool/index.d.ts.map +0 -1
  470. package/dest/mem_pools/tx_pool/index.js +0 -2
  471. package/dest/mem_pools/tx_pool/priority.d.ts +0 -12
  472. package/dest/mem_pools/tx_pool/priority.d.ts.map +0 -1
  473. package/dest/mem_pools/tx_pool/priority.js +0 -15
  474. package/dest/mem_pools/tx_pool/tx_pool.d.ts +0 -127
  475. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +0 -1
  476. package/dest/mem_pools/tx_pool/tx_pool.js +0 -3
  477. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +0 -7
  478. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +0 -1
  479. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +0 -400
  480. package/dest/msg_validators/clock_tolerance.d.ts +0 -21
  481. package/dest/msg_validators/clock_tolerance.d.ts.map +0 -1
  482. package/dest/msg_validators/clock_tolerance.js +0 -37
  483. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts +0 -23
  484. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts.map +0 -1
  485. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.js +0 -212
  486. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +0 -64
  487. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +0 -1
  488. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.js +0 -151
  489. package/dest/services/reqresp/protocols/block.d.ts +0 -9
  490. package/dest/services/reqresp/protocols/block.d.ts.map +0 -1
  491. package/dest/services/reqresp/protocols/block.js +0 -32
  492. package/dest/services/tx_collection/fast_tx_collection.d.ts +0 -52
  493. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +0 -1
  494. package/dest/services/tx_collection/fast_tx_collection.js +0 -307
  495. package/dest/services/tx_collection/proposal_tx_collector.d.ts +0 -48
  496. package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +0 -1
  497. package/dest/services/tx_collection/proposal_tx_collector.js +0 -49
  498. package/dest/services/tx_collection/slow_tx_collection.d.ts +0 -53
  499. package/dest/services/tx_collection/slow_tx_collection.d.ts.map +0 -1
  500. package/dest/services/tx_collection/slow_tx_collection.js +0 -177
  501. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +0 -336
  502. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.ts +0 -43
  503. package/src/mem_pools/tx_pool/README.md +0 -270
  504. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +0 -746
  505. package/src/mem_pools/tx_pool/eviction/eviction_manager.ts +0 -132
  506. package/src/mem_pools/tx_pool/eviction/eviction_strategy.ts +0 -208
  507. package/src/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.ts +0 -162
  508. package/src/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.ts +0 -104
  509. package/src/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.ts +0 -93
  510. package/src/mem_pools/tx_pool/eviction/low_priority_eviction_rule.ts +0 -106
  511. package/src/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.ts +0 -75
  512. package/src/mem_pools/tx_pool/index.ts +0 -2
  513. package/src/mem_pools/tx_pool/priority.ts +0 -20
  514. package/src/mem_pools/tx_pool/tx_pool.ts +0 -141
  515. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +0 -319
  516. package/src/msg_validators/clock_tolerance.ts +0 -51
  517. package/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts +0 -230
  518. package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +0 -161
  519. package/src/services/reqresp/protocols/block.ts +0 -37
  520. package/src/services/tx_collection/fast_tx_collection.ts +0 -354
  521. package/src/services/tx_collection/proposal_tx_collector.ts +0 -112
  522. package/src/services/tx_collection/slow_tx_collection.ts +0 -233
@@ -1,174 +1,242 @@
1
- import { Fr } from '@aztec/foundation/curves/bn254';
2
1
  import { toArray } from '@aztec/foundation/iterable';
3
2
  import { createLogger } from '@aztec/foundation/log';
4
3
  import { BlockProposal, CheckpointAttestation, CheckpointProposal } from '@aztec/stdlib/p2p';
5
4
  import { getTelemetryClient } from '@aztec/telemetry-client';
6
5
  import { PoolInstrumentation, PoolName } from '../instrumentation.js';
7
- export const MAX_PROPOSALS_PER_SLOT = 5;
8
- export const MAX_PROPOSALS_PER_POSITION = 3;
9
- export const ATTESTATION_CAP_BUFFER = 10;
6
+ export const MAX_CHECKPOINT_PROPOSALS_PER_SLOT = 2;
7
+ export const MAX_BLOCK_PROPOSALS_PER_POSITION = 2;
8
+ /** Maximum attestations a single signer can make per slot before being rejected. */ export const MAX_ATTESTATIONS_PER_SLOT_AND_SIGNER = 2;
10
9
  /**
11
10
  * Pool for storing attestations and proposals collected by a validator.
12
11
  *
13
12
  * Attestations and proposals observed via the p2p network are stored for requests
14
13
  * from the validator to produce a block, or to serve to other peers.
14
+ *
15
+ * Equivocation detection: distinct *signed payload hashes* arriving at the same
16
+ * position are tracked in the matching index multimap so the equivocation count
17
+ * reaches 2 even when archive collides on `feeAssetPriceModifier` variants.
18
+ * Proposal bytes are retained per accepted payload hash, up to the same equivocation
19
+ * caps, for slashing watchers that need signed P2P proposals.
15
20
  */ export class AttestationPool {
16
21
  store;
17
22
  log;
18
23
  metrics;
19
- // Checkpoint attestations from attestation key (slot-proposalId-signer) to serialized CheckpointAttestation
20
- // Keys are lexicographically sortable allowing range queries by slot or by (slot, proposalId)
21
- checkpointAttestations;
22
- // Checkpoint proposals from proposal archive to serialized CheckpointProposal
23
- checkpointProposals;
24
- // Checkpoint proposals indexed by slot for querying all proposals in a slot
25
- // Key: slot number, Value: proposal archive strings
26
- checkpointProposalsForSlot;
27
- // Block proposals from proposal archive to serialized BlockProposal
28
- blockProposals;
29
- // Block proposals indexed by slot and index-within-checkpoint for duplicate detection
30
- // Key: (slot << 10) | indexWithinCheckpoint, Value: archive string
31
- blockProposalsForSlotAndIndex;
24
+ // Checkpoint attestations from `${paddedSlot}-${signer}` to serialized CheckpointAttestation.
25
+ // Stores the first attestation seen per (slot, signer); subsequent distinct payload
26
+ // hashes from the same signer are tracked only in `attestationHashesPerSlotAndSigner`
27
+ // for equivocation detection.
28
+ attestationPerSlotAndSigner;
29
+ // Distinct payload hashes seen per (slot, signer) for tracking attestation equivocations.
30
+ // Key: `${paddedSlot}-${signerAddress}`, Value: CheckpointProposalHash (`0x`-prefixed hex)
31
+ attestationHashesPerSlotAndSigner;
32
+ // Checkpoint proposals from `${paddedSlot}-${payloadHash}` to serialized CheckpointProposalCore.
33
+ // Stores every accepted distinct payload up to MAX_CHECKPOINT_PROPOSALS_PER_SLOT.
34
+ checkpointProposalsPerSlotAndHash;
35
+ // Distinct payload hashes seen per slot. Hash collision = duplicate.
36
+ // Hash count reaching 2 = equivocation.
37
+ // Key: slot number, Value: CheckpointProposalHash (`0x`-prefixed hex)
38
+ checkpointProposalHashesPerSlot;
39
+ // Block proposals from `${paddedSlot}-${paddedIndex}-${payloadHash}` to serialized BlockProposal.
40
+ // Stores every accepted distinct payload up to MAX_BLOCK_PROPOSALS_PER_POSITION.
41
+ blockProposalsPerSlotIndexAndHash;
42
+ // Distinct payload hashes seen per (slot, indexWithinCheckpoint).
43
+ // Key: slot * (1 << INDEX_BITS) + indexWithinCheckpoint, Value: BlockProposalHash (`0x`-prefixed hex)
44
+ blockProposalHashesPerSlotAndIndex;
45
+ // Secondary index from archive root to all retained block proposal keys.
46
+ blockProposalKeysPerArchive;
32
47
  constructor(store, telemetry = getTelemetryClient(), log = createLogger('aztec:attestation_pool')){
33
48
  this.store = store;
34
49
  this.log = log;
35
50
  this.poolStats = async ()=>{
36
51
  return {
37
- itemCount: await this.checkpointAttestations.sizeAsync()
52
+ itemCount: await this.attestationPerSlotAndSigner.sizeAsync()
38
53
  };
39
54
  };
40
55
  // Initialize block proposal storage
41
- this.blockProposals = store.openMap('proposals');
42
- this.blockProposalsForSlotAndIndex = store.openMultiMap('block_proposals_for_slot_and_index');
56
+ this.blockProposalsPerSlotIndexAndHash = store.openMap('block_proposals_by_slot_index_and_hash');
57
+ this.blockProposalHashesPerSlotAndIndex = store.openMultiMap('block_proposals_for_slot_and_index');
58
+ this.blockProposalKeysPerArchive = store.openMultiMap('block_proposals_by_archive');
43
59
  // Initialize checkpoint attestations storage
44
- this.checkpointAttestations = store.openMap('checkpoint_attestations');
60
+ this.attestationPerSlotAndSigner = store.openMap('checkpoint_attestations');
61
+ this.attestationHashesPerSlotAndSigner = store.openMultiMap('checkpoint_attestations_per_slot_and_signer');
45
62
  // Initialize checkpoint proposal storage
46
- this.checkpointProposals = store.openMap('checkpoint_proposals');
47
- this.checkpointProposalsForSlot = store.openMultiMap('checkpoint_proposals_for_slot');
63
+ this.checkpointProposalsPerSlotAndHash = store.openMap('checkpoint_proposals_by_slot_and_hash');
64
+ this.checkpointProposalHashesPerSlot = store.openMultiMap('checkpoint_proposals_for_slot');
48
65
  this.metrics = new PoolInstrumentation(telemetry, PoolName.ATTESTATION_POOL, this.poolStats);
49
66
  }
50
67
  poolStats;
51
68
  /** Returns whether the pool is empty. */ async isEmpty() {
52
- for await (const _ of this.checkpointAttestations.entriesAsync()){
53
- return false;
54
- }
55
- for await (const _ of this.blockProposals.entriesAsync()){
56
- return false;
57
- }
58
- return true;
69
+ const [attestationCount, blockProposalCount, checkpointProposalCount] = await Promise.all([
70
+ this.attestationPerSlotAndSigner.sizeAsync(),
71
+ this.blockProposalsPerSlotIndexAndHash.sizeAsync(),
72
+ this.checkpointProposalsPerSlotAndHash.sizeAsync()
73
+ ]);
74
+ return attestationCount === 0 && blockProposalCount === 0 && checkpointProposalCount === 0;
59
75
  }
60
- getProposalKey(slot, proposalId) {
61
- const slotStr = typeof slot === 'string' ? slot : new Fr(slot).toString();
62
- const proposalIdStr = typeof proposalId === 'string' ? proposalId : Buffer.isBuffer(proposalId) ? Fr.fromBuffer(proposalId).toString() : proposalId.toString();
63
- return `${slotStr}-${proposalIdStr}`;
76
+ /** Number of bits reserved for indexWithinCheckpoint in position keys. */ static INDEX_BITS = 10;
77
+ /** Maximum indexWithinCheckpoint value (2^10 - 1 = 1023). */ static MAX_INDEX = (1 << AttestationPool.INDEX_BITS) - 1;
78
+ /** Decimal digits used to left-pad slot numbers in string keys.
79
+ * 10 digits ≈ 3500 years at 36 s/slot, leaving ample headroom. */ static SLOT_PAD_DIGITS = 10;
80
+ /** Fixed-width decimal slot string for use in composite string keys. */ slotPaddedKey(slot) {
81
+ return slot.toString().padStart(AttestationPool.SLOT_PAD_DIGITS, '0');
82
+ }
83
+ /** Fixed-width decimal index string for use in composite string keys. */ indexPaddedKey(indexWithinCheckpoint) {
84
+ return indexWithinCheckpoint.toString().padStart(4, '0');
64
85
  }
65
- getAttestationKey(slot, proposalId, address) {
66
- return `${this.getProposalKey(slot, proposalId)}-${address}`;
86
+ /** Key for retained block proposals. */ getBlockProposalKey(slot, indexWithinCheckpoint, payloadHash) {
87
+ return `${this.slotPaddedKey(slot)}-${this.indexPaddedKey(indexWithinCheckpoint)}-${payloadHash}`;
67
88
  }
68
- /** Returns range bounds for querying all attestations for a given slot. */ getAttestationKeyRangeForSlot(slot) {
69
- const slotStr = new Fr(slot).toString();
89
+ /** Range bounds for all retained block proposals in a slot. */ getBlockProposalKeyRangeForSlot(slot) {
70
90
  return {
71
- start: `${slotStr}-`,
72
- end: `${slotStr}-Z`
73
- }; // 'Z' sorts after any hex character
91
+ start: `${this.slotPaddedKey(slot)}-`,
92
+ end: `${this.slotPaddedKey(slot + 1)}-`
93
+ };
74
94
  }
75
- /** Returns range bounds for querying all attestations for a given (slot, proposalId). */ getAttestationKeyRangeForProposal(slot, proposalId) {
76
- const proposalKey = this.getProposalKey(slot, proposalId);
95
+ /** Key for retained checkpoint proposals. */ getCheckpointProposalKey(slot, payloadHash) {
96
+ return `${this.slotPaddedKey(slot)}-${payloadHash}`;
97
+ }
98
+ /** Range bounds for all retained checkpoint proposals in a slot. */ getCheckpointProposalKeyRangeForSlot(slot) {
77
99
  return {
78
- start: `${proposalKey}-`,
79
- end: `${proposalKey}-Z`
100
+ start: `${this.slotPaddedKey(slot)}-`,
101
+ end: `${this.slotPaddedKey(slot + 1)}-`
80
102
  };
81
103
  }
82
- /** Number of bits reserved for indexWithinCheckpoint in position keys. */ static INDEX_BITS = 10;
83
- /** Maximum indexWithinCheckpoint value (2^10 - 1 = 1023). */ static MAX_INDEX = (1 << AttestationPool.INDEX_BITS) - 1;
84
- /** Creates a position key for block proposals: (slot << 10) | indexWithinCheckpoint. */ getBlockPositionKey(slot, indexWithinCheckpoint) {
104
+ /** Key for the per-(slot, signer) attestation main store and equivocation index. */ getSlotSignerKey(slot, signerAddress) {
105
+ return `${this.slotPaddedKey(slot)}-${signerAddress}`;
106
+ }
107
+ /**
108
+ * Returns range bounds for querying all attestations for a given slot.
109
+ * Fixed-width padding ensures the slot prefix sorts cleanly, so using the next
110
+ * slot's prefix as the upper bound captures exactly the current slot's entries.
111
+ */ getAttestationKeyRangeForSlot(slot) {
112
+ return {
113
+ start: `${this.slotPaddedKey(slot)}-`,
114
+ end: `${this.slotPaddedKey(slot + 1)}-`
115
+ };
116
+ }
117
+ /** Creates a position key for block proposals: slot * 1024 + indexWithinCheckpoint.
118
+ * Uses multiplication instead of bit-shift to avoid 32-bit signed integer overflow
119
+ * (bit-shift overflows after slot ~2^21, roughly 278 days of uptime). */ getBlockPositionKey(slot, indexWithinCheckpoint) {
85
120
  if (indexWithinCheckpoint > AttestationPool.MAX_INDEX) {
86
121
  throw new Error(`Value for indexWithinCheckpoint ${indexWithinCheckpoint} exceeds maximum ${AttestationPool.MAX_INDEX}`);
87
122
  }
88
- return slot << AttestationPool.INDEX_BITS | indexWithinCheckpoint;
123
+ return slot * (1 << AttestationPool.INDEX_BITS) + indexWithinCheckpoint;
124
+ }
125
+ /** Returns true if the multimap already contains the given value for the given key. */ async multimapHasValue(map, key, value) {
126
+ const values = await toArray(map.getValuesAsync(key));
127
+ return values.includes(value);
89
128
  }
90
129
  /**
91
130
  * Attempts to add a block proposal to the pool.
92
131
  *
93
- * This method performs validation and addition in a single call:
94
- * - Checks if the proposal already exists (returns alreadyExists: true if so)
95
- * - Checks if the position has reached the proposal cap (returns added: false if so)
96
- * - Adds the proposal if validation passes
132
+ * - Detects duplicates by signed-payload hash (not archive); a re-broadcast of the
133
+ * exact same signed payload returns `alreadyExists: true`.
134
+ * - Distinct payload hashes at the same `(slot, indexWithinCheckpoint)` are tracked
135
+ * in the equivocation index and retained up to the cap.
97
136
  *
98
137
  * @param blockProposal - The block proposal to add
99
138
  * @returns Result indicating whether the proposal was added and duplicate detection info
100
139
  */ async tryAddBlockProposal(blockProposal) {
101
140
  return await this.store.transactionAsync(async ()=>{
102
- const proposalId = blockProposal.archive.toString();
103
- // Check if already exists
104
- const alreadyExists = await this.blockProposals.hasAsync(proposalId);
105
- if (alreadyExists) {
106
- const totalForPosition = await this.getBlockProposalCountForPosition(blockProposal.slotNumber, blockProposal.indexWithinCheckpoint);
141
+ const positionKey = this.getBlockPositionKey(blockProposal.slotNumber, blockProposal.indexWithinCheckpoint);
142
+ const payloadHash = blockProposal.getPayloadHash();
143
+ // Hash already tracked => exact same signed payload was already received.
144
+ if (await this.multimapHasValue(this.blockProposalHashesPerSlotAndIndex, positionKey, payloadHash)) {
145
+ const count = await this.blockProposalHashesPerSlotAndIndex.getValueCountAsync(positionKey);
107
146
  return {
108
147
  added: false,
109
148
  alreadyExists: true,
110
- totalForPosition
149
+ count
111
150
  };
112
151
  }
113
- // Get current count for position and check cap, do not add if exceeded
114
- const totalForPosition = await this.getBlockProposalCountForPosition(blockProposal.slotNumber, blockProposal.indexWithinCheckpoint);
115
- if (totalForPosition >= MAX_PROPOSALS_PER_POSITION) {
152
+ // Cap reached for this position (no more new payload hashes accepted).
153
+ const count = await this.blockProposalHashesPerSlotAndIndex.getValueCountAsync(positionKey);
154
+ if (count >= MAX_BLOCK_PROPOSALS_PER_POSITION) {
116
155
  return {
117
156
  added: false,
118
157
  alreadyExists: false,
119
- totalForPosition
158
+ count
120
159
  };
121
160
  }
122
- // Add the proposal
123
- await this.addBlockProposal(blockProposal);
161
+ // Track the new payload hash for equivocation detection.
162
+ await this.blockProposalHashesPerSlotAndIndex.set(positionKey, payloadHash);
163
+ const proposalKey = this.getBlockProposalKey(blockProposal.slotNumber, blockProposal.indexWithinCheckpoint, payloadHash);
164
+ await this.blockProposalsPerSlotIndexAndHash.set(proposalKey, blockProposal.withoutSignedTxs().toBuffer());
165
+ await this.blockProposalKeysPerArchive.set(blockProposal.archive.toString(), proposalKey);
124
166
  this.log.debug(`Added block proposal for slot ${blockProposal.slotNumber} and index ${blockProposal.indexWithinCheckpoint}`, {
125
- proposalId,
167
+ archive: blockProposal.archive.toString(),
168
+ payloadHash,
126
169
  slotNumber: blockProposal.slotNumber,
127
170
  indexWithinCheckpoint: blockProposal.indexWithinCheckpoint
128
171
  });
129
172
  return {
130
173
  added: true,
131
174
  alreadyExists: false,
132
- totalForPosition: totalForPosition + 1
175
+ count: count + 1
133
176
  };
134
177
  });
135
178
  }
136
- /** Gets the count of block proposals for a given position (slot, indexWithinCheckpoint). */ getBlockProposalCountForPosition(slot, indexWithinCheckpoint) {
137
- const positionKey = this.getBlockPositionKey(slot, indexWithinCheckpoint);
138
- return this.blockProposalsForSlotAndIndex.getValueCountAsync(positionKey);
139
- }
140
- /** Internal method - must be called within a transaction. */ async addBlockProposal(blockProposal) {
141
- const proposalId = blockProposal.archive.toString();
142
- // Strip signedTxs before storing to avoid persisting full tx data
143
- await this.blockProposals.set(proposalId, blockProposal.withoutSignedTxs().toBuffer());
144
- // Index by slot and position for duplicate detection
145
- const positionKey = this.getBlockPositionKey(blockProposal.slotNumber, blockProposal.indexWithinCheckpoint);
146
- await this.blockProposalsForSlotAndIndex.set(positionKey, proposalId);
147
- }
148
179
  /**
149
- * Get block proposal by its ID.
180
+ * Get block proposal by archive root.
150
181
  *
151
- * @param id - The ID of the block proposal to retrieve. The ID is proposal.payload.archive
182
+ * Resolves the archive root through the archive index and returns the first
183
+ * retained proposal for that archive. This lookup is used by block-txs req/resp,
184
+ * where any retained proposal for the requested archive gives the tx hash list.
152
185
  *
153
- * @return The block proposal if it exists, otherwise undefined.
154
- */ async getBlockProposal(id) {
155
- const buffer = await this.blockProposals.getAsync(id);
156
- try {
157
- if (buffer && buffer.length > 0) {
158
- return BlockProposal.fromBuffer(buffer);
186
+ * @param archiveRoot - The archive root to look up
187
+ * @return The block proposal if it exists and its archive matches, otherwise undefined.
188
+ */ async getBlockProposalByArchive(archiveRoot) {
189
+ for await (const proposalKey of this.blockProposalKeysPerArchive.getValuesAsync(archiveRoot)){
190
+ const buffer = await this.blockProposalsPerSlotIndexAndHash.getAsync(proposalKey);
191
+ if (!buffer || buffer.length === 0) {
192
+ continue;
193
+ }
194
+ try {
195
+ const proposal = BlockProposal.fromBuffer(buffer);
196
+ if (proposal.archive.toString() === archiveRoot) {
197
+ return proposal;
198
+ }
199
+ } catch {
200
+ continue;
159
201
  }
160
- } catch {
161
- return undefined;
162
202
  }
163
203
  return undefined;
164
204
  }
205
+ /** Returns retained signed proposals for a slot. */ async getProposalsForSlot(slot) {
206
+ const blockProposals = [];
207
+ const checkpointProposals = [];
208
+ for await (const [_, buffer] of this.blockProposalsPerSlotIndexAndHash.entriesAsync(this.getBlockProposalKeyRangeForSlot(slot))){
209
+ try {
210
+ blockProposals.push(BlockProposal.fromBuffer(buffer));
211
+ } catch {
212
+ continue;
213
+ }
214
+ }
215
+ for await (const [_, buffer] of this.checkpointProposalsPerSlotAndHash.entriesAsync(this.getCheckpointProposalKeyRangeForSlot(slot))){
216
+ try {
217
+ checkpointProposals.push(CheckpointProposal.fromBuffer(buffer));
218
+ } catch {
219
+ continue;
220
+ }
221
+ }
222
+ return {
223
+ blockProposals,
224
+ checkpointProposals
225
+ };
226
+ }
227
+ /** Checks if any block proposals exist for a given slot (at index 0). */ async hasBlockProposalsForSlot(slot) {
228
+ const positionKey = this.getBlockPositionKey(slot, 0);
229
+ const count = await this.blockProposalHashesPerSlotAndIndex.getValueCountAsync(positionKey);
230
+ return count > 0;
231
+ }
165
232
  /**
166
233
  * Attempts to add a checkpoint proposal to the pool.
167
234
  *
168
- * This method performs validation and addition in a single call:
169
- * - Checks if the proposal already exists (returns alreadyExists: true if so)
170
- * - Checks if the slot has reached the proposal cap (returns added: false if so)
171
- * - Adds the proposal if validation passes
235
+ * - Detects duplicates by signed-payload hash (not archive); a re-broadcast of the
236
+ * exact same signed payload returns `alreadyExists: true`.
237
+ * - Distinct payload hashes at the same slot are tracked in the equivocation index.
238
+ * Distinct payload bytes are retained up to the same cap so slashing watchers
239
+ * can recover signed proposals.
172
240
  *
173
241
  * Note: This method only handles the CheckpointProposalCore. If the original
174
242
  * CheckpointProposal contains a lastBlock, the caller should extract it via
@@ -178,89 +246,99 @@ export const ATTESTATION_CAP_BUFFER = 10;
178
246
  * @returns Result indicating whether the proposal was added and duplicate detection info
179
247
  */ async tryAddCheckpointProposal(proposal) {
180
248
  return await this.store.transactionAsync(async ()=>{
181
- const proposalId = proposal.archive.toString();
182
- // Check if already exists
183
- const alreadyExists = await this.checkpointProposals.hasAsync(proposalId);
184
- if (alreadyExists) {
185
- const totalForPosition = await this.checkpointProposalsForSlot.getValueCountAsync(proposal.slotNumber);
249
+ const slot = proposal.slotNumber;
250
+ const payloadHash = proposal.getPayloadHash();
251
+ if (await this.multimapHasValue(this.checkpointProposalHashesPerSlot, slot, payloadHash)) {
252
+ const count = await this.checkpointProposalHashesPerSlot.getValueCountAsync(slot);
186
253
  return {
187
254
  added: false,
188
255
  alreadyExists: true,
189
- totalForPosition
256
+ count
190
257
  };
191
258
  }
192
- // Get current count for slot and check cap
193
- const totalForPosition = await this.checkpointProposalsForSlot.getValueCountAsync(proposal.slotNumber);
194
- if (totalForPosition >= MAX_PROPOSALS_PER_SLOT) {
259
+ const count = await this.checkpointProposalHashesPerSlot.getValueCountAsync(slot);
260
+ if (count >= MAX_CHECKPOINT_PROPOSALS_PER_SLOT) {
195
261
  return {
196
262
  added: false,
197
263
  alreadyExists: false,
198
- totalForPosition
264
+ count
199
265
  };
200
266
  }
201
- // Add the proposal if cap not exceeded
202
- await this.addCheckpointProposal(proposal);
203
- this.log.debug(`Added checkpoint proposal for slot ${proposal.slotNumber}`, {
204
- proposalId,
205
- slotNumber: proposal.slotNumber
267
+ // Track the new payload hash for equivocation detection.
268
+ await this.checkpointProposalHashesPerSlot.set(slot, payloadHash);
269
+ await this.checkpointProposalsPerSlotAndHash.set(this.getCheckpointProposalKey(slot, payloadHash), proposal.toBuffer());
270
+ this.log.debug(`Added checkpoint proposal for slot ${slot}`, {
271
+ archive: proposal.archive.toString(),
272
+ payloadHash,
273
+ slotNumber: slot
206
274
  });
207
275
  return {
208
276
  added: true,
209
277
  alreadyExists: false,
210
- totalForPosition: totalForPosition + 1
278
+ count: count + 1
211
279
  };
212
280
  });
213
281
  }
214
- /** Internal method - must be called within a transaction. */ async addCheckpointProposal(proposal) {
215
- const slotKey = proposal.slotNumber;
216
- const proposalId = proposal.archive.toString();
217
- await this.checkpointProposalsForSlot.set(slotKey, proposalId);
218
- await this.checkpointProposals.set(proposalId, proposal.toBuffer());
219
- }
220
282
  /**
221
- * Get checkpoint proposal by its ID.
283
+ * Get a retained checkpoint proposal stored for the given slot.
284
+ * If multiple proposals were retained for an equivocation, returns the lowest
285
+ * payload hash deterministically.
222
286
  *
223
287
  * Returns a CheckpointProposalCore (without lastBlock info) since the lastBlock
224
288
  * is extracted and stored separately as a BlockProposal when added.
225
289
  *
226
- * @param id - The ID of the checkpoint proposal to retrieve (proposal.archive)
227
- * @return The checkpoint proposal core if it exists, otherwise undefined.
228
- */ async getCheckpointProposal(id) {
229
- const buffer = await this.checkpointProposals.getAsync(id);
230
- try {
231
- if (buffer && buffer.length > 0) {
232
- return CheckpointProposal.fromBuffer(buffer);
290
+ * @param slot - The slot to look up
291
+ * @return The checkpoint proposal core if one is stored, otherwise undefined.
292
+ */ async getCheckpointProposal(slot) {
293
+ for await (const [_, buffer] of this.checkpointProposalsPerSlotAndHash.entriesAsync(this.getCheckpointProposalKeyRangeForSlot(slot))){
294
+ try {
295
+ if (buffer && buffer.length > 0) {
296
+ return CheckpointProposal.fromBuffer(buffer);
297
+ }
298
+ } catch {
299
+ continue;
233
300
  }
234
- } catch {
235
- return undefined;
236
301
  }
237
302
  return undefined;
238
303
  }
304
+ /** Returns whether any checkpoint proposal is retained for the given slot. */ async hasCheckpointProposalForSlot(slot) {
305
+ return await this.getCheckpointProposal(slot) !== undefined;
306
+ }
239
307
  /**
240
308
  * Adds own checkpoint attestations to the pool.
241
- * Skips validations on number of checkpoint attestations stored for the given slot.
309
+ * Skips per-signer cap and equivocation tracking; the caller is trusted.
310
+ * Each (slot, signer) gets a single stored attestation; later additions overwrite.
242
311
  */ async addOwnCheckpointAttestations(attestations) {
243
312
  await this.store.transactionAsync(async ()=>{
244
313
  for (const attestation of attestations){
245
314
  const slotNumber = attestation.payload.header.slotNumber;
246
- const proposalId = attestation.archive;
247
315
  const sender = attestation.getSender();
248
316
  // Skip attestations with invalid signatures
249
317
  if (!sender) {
250
318
  this.log.warn(`Skipping own checkpoint attestation with invalid signature for slot ${slotNumber}`, {
251
319
  signature: attestation.signature.toString(),
252
320
  slotNumber,
253
- proposalId
321
+ archive: attestation.archive.toString()
254
322
  });
255
323
  continue;
256
324
  }
257
325
  const address = sender.toString();
258
- await this.checkpointAttestations.set(this.getAttestationKey(slotNumber, proposalId, address), attestation.toBuffer());
326
+ const ownKey = this.getSlotSignerKey(slotNumber, address);
327
+ const payloadHash = attestation.getPayloadHash();
328
+ // Store the signature in canonical (v ∈ {27, 28}) form; see tryAddCheckpointAttestation.
329
+ await this.attestationPerSlotAndSigner.set(ownKey, attestation.withNormalizedSignature().toBuffer());
330
+ this.metrics.trackMempoolItemAdded(ownKey);
331
+ // Track our own payload hash so that an equivocating attestation from another
332
+ // peer at the same (slot, signer) is detected as a duplicate.
333
+ if (!await this.multimapHasValue(this.attestationHashesPerSlotAndSigner, ownKey, payloadHash)) {
334
+ await this.attestationHashesPerSlotAndSigner.set(ownKey, payloadHash);
335
+ }
259
336
  this.log.debug(`Added own checkpoint attestation for slot ${slotNumber} from ${address}`, {
260
337
  signature: attestation.signature.toString(),
261
338
  slotNumber,
262
339
  address,
263
- proposalId
340
+ archive: attestation.archive.toString(),
341
+ payloadHash
264
342
  });
265
343
  }
266
344
  });
@@ -268,29 +346,30 @@ export const ATTESTATION_CAP_BUFFER = 10;
268
346
  /**
269
347
  * Get all checkpoint attestations for a given slot.
270
348
  *
349
+ * Returns one attestation per (slot, signer) — the first seen for each signer.
350
+ * Later equivocating attestations from the same signer are tracked in the index
351
+ * but their bytes are not retained.
352
+ *
271
353
  * @param slot - The slot to query
272
354
  * @return CheckpointAttestations
273
355
  */ async getCheckpointAttestationsForSlot(slot) {
274
356
  const range = this.getAttestationKeyRangeForSlot(slot);
275
357
  const attestations = [];
276
- for await (const [_, buf] of this.checkpointAttestations.entriesAsync(range)){
358
+ for await (const [_, buf] of this.attestationPerSlotAndSigner.entriesAsync(range)){
277
359
  attestations.push(CheckpointAttestation.fromBuffer(buf));
278
360
  }
279
361
  return attestations;
280
362
  }
281
363
  /**
282
- * Get checkpoint attestations for slot and given proposal.
364
+ * Get checkpoint attestations for a slot whose signed payload matches the given
365
+ * proposal payload hash.
283
366
  *
284
367
  * @param slot - The slot to query
285
- * @param proposalId - The proposal to query
286
- * @return CheckpointAttestations
287
- */ async getCheckpointAttestationsForSlotAndProposal(slot, proposalId) {
288
- const range = this.getAttestationKeyRangeForProposal(slot, proposalId);
289
- const attestations = [];
290
- for await (const [_, buf] of this.checkpointAttestations.entriesAsync(range)){
291
- attestations.push(CheckpointAttestation.fromBuffer(buf));
292
- }
293
- return attestations;
368
+ * @param proposalPayloadHash - Hex-encoded keccak256 of the target proposal's signed payload
369
+ * @return CheckpointAttestations whose `getPayloadHash()` matches `proposalPayloadHash`
370
+ */ async getCheckpointAttestationsForSlotAndProposal(slot, proposalPayloadHash) {
371
+ const all = await this.getCheckpointAttestationsForSlot(slot);
372
+ return all.filter((att)=>att.getPayloadHash() === proposalPayloadHash);
294
373
  }
295
374
  /**
296
375
  * Delete all pool data (attestations, proposals) older than the given slot.
@@ -301,38 +380,53 @@ export const ATTESTATION_CAP_BUFFER = 10;
301
380
  let numberOfCheckpointProposals = 0;
302
381
  let numberOfBlockProposals = 0;
303
382
  await this.store.transactionAsync(async ()=>{
304
- // Delete checkpoint attestations with slot < oldestSlot
305
- // Attestation keys start with Fr(slot).toString(), so we use end bound of Fr(oldestSlot).toString()
306
- const attestationEndKey = new Fr(oldestSlot).toString();
307
- for await (const key of this.checkpointAttestations.keysAsync({
308
- end: attestationEndKey
383
+ const oldestSlotPadded = this.slotPaddedKey(oldestSlot);
384
+ // Delete checkpoint attestations whose key < `${oldestSlotPadded}-`. Fixed-width
385
+ // decimal padding means the slot prefix sorts strictly before any key at that slot.
386
+ for await (const key of this.attestationPerSlotAndSigner.keysAsync({
387
+ end: `${oldestSlotPadded}-`
309
388
  })){
310
- await this.checkpointAttestations.delete(key);
389
+ await this.attestationPerSlotAndSigner.delete(key);
390
+ this.metrics.trackMempoolItemRemoved(key);
311
391
  numberOfAttestations++;
312
392
  }
313
- // Delete checkpoint proposals for slots < oldestSlot, using checkpointProposalsForSlot as index
314
- for await (const slot of this.checkpointProposalsForSlot.keysAsync({
393
+ // Clean up per-signer-per-slot index using the same end bound.
394
+ for await (const key of this.attestationHashesPerSlotAndSigner.keysAsync({
395
+ end: `${oldestSlotPadded}-`
396
+ })){
397
+ await this.attestationHashesPerSlotAndSigner.delete(key);
398
+ }
399
+ // Delete checkpoint proposals for slots < oldestSlot.
400
+ for await (const slot of this.checkpointProposalHashesPerSlot.keysAsync({
315
401
  end: oldestSlot
316
402
  })){
317
- const proposalIds = await toArray(this.checkpointProposalsForSlot.getValuesAsync(slot));
318
- for (const proposalId of proposalIds){
319
- await this.checkpointProposals.delete(proposalId);
320
- numberOfCheckpointProposals++;
321
- }
322
- await this.checkpointProposalsForSlot.delete(slot);
403
+ await this.checkpointProposalHashesPerSlot.delete(slot);
323
404
  }
324
- // Delete block proposals for slots < oldestSlot, using blockProposalsForSlotAndIndex as index
325
- // Key format: (slot << INDEX_BITS) | indexWithinCheckpoint
326
- const blockPositionEndKey = oldestSlot << AttestationPool.INDEX_BITS;
327
- for await (const positionKey of this.blockProposalsForSlotAndIndex.keysAsync({
405
+ for await (const key of this.checkpointProposalsPerSlotAndHash.keysAsync({
406
+ end: `${oldestSlotPadded}-`
407
+ })){
408
+ await this.checkpointProposalsPerSlotAndHash.delete(key);
409
+ numberOfCheckpointProposals++;
410
+ }
411
+ // Delete block proposals for slots < oldestSlot, using blockProposalHashesPerSlotAndIndex as index.
412
+ // Key format: slot * (1 << INDEX_BITS) + indexWithinCheckpoint
413
+ const blockPositionEndKey = oldestSlot * (1 << AttestationPool.INDEX_BITS);
414
+ for await (const positionKey of this.blockProposalHashesPerSlotAndIndex.keysAsync({
328
415
  end: blockPositionEndKey
329
416
  })){
330
- const proposalIds = await toArray(this.blockProposalsForSlotAndIndex.getValuesAsync(positionKey));
331
- for (const proposalId of proposalIds){
332
- await this.blockProposals.delete(proposalId);
333
- numberOfBlockProposals++;
417
+ await this.blockProposalHashesPerSlotAndIndex.delete(positionKey);
418
+ }
419
+ for await (const [key, buffer] of this.blockProposalsPerSlotIndexAndHash.entriesAsync({
420
+ end: `${oldestSlotPadded}-`
421
+ })){
422
+ try {
423
+ const proposal = BlockProposal.fromBuffer(buffer);
424
+ await this.blockProposalKeysPerArchive.deleteValue(proposal.archive.toString(), key);
425
+ } catch {
426
+ // ignore decode errors when cleaning up
334
427
  }
335
- await this.blockProposalsForSlotAndIndex.delete(positionKey);
428
+ await this.blockProposalsPerSlotIndexAndHash.delete(key);
429
+ numberOfBlockProposals++;
336
430
  }
337
431
  });
338
432
  this.log.verbose(`Deleted old pool data`, {
@@ -345,67 +439,80 @@ export const ATTESTATION_CAP_BUFFER = 10;
345
439
  /**
346
440
  * Attempts to add a checkpoint attestation to the pool.
347
441
  *
348
- * This method performs validation and addition in a single call:
349
- * - Checks if the attestation already exists (returns alreadyExists: true if so)
350
- * - Checks if the (slot, proposalId) has reached the attestation cap (returns added: false if so)
351
- * - Adds the attestation if validation passes
442
+ * - Detects duplicates by signed-payload hash (not archive); a re-broadcast of the
443
+ * exact same signed payload from the same signer returns `alreadyExists: true`.
444
+ * - Distinct payload hashes from the same (slot, signer) are tracked in the
445
+ * equivocation index. The first one's bytes are stored; later distinct hashes
446
+ * bump `count` so libp2p can fire its duplicate callback.
352
447
  *
353
448
  * @param attestation - The checkpoint attestation to add
354
- * @param committeeSize - Committee size for the attestation's slot
355
- * @returns Result indicating whether the attestation was added and existence info
356
- */ async tryAddCheckpointAttestation(attestation, committeeSize) {
449
+ * @returns Result indicating whether the attestation was added, existence info,
450
+ * and number of distinct payload hashes by this signer for this slot
451
+ * (for equivocation detection).
452
+ */ async tryAddCheckpointAttestation(attestation) {
357
453
  const slotNumber = attestation.payload.header.slotNumber;
358
- const proposalId = attestation.archive.toString();
359
454
  const sender = attestation.getSender();
360
455
  if (!sender) {
361
456
  return {
362
457
  added: false,
363
458
  alreadyExists: false,
364
- totalForPosition: 0
459
+ count: 0
365
460
  };
366
461
  }
462
+ const signerAddress = sender.toString();
463
+ const slotSignerKey = this.getSlotSignerKey(slotNumber, signerAddress);
464
+ const payloadHash = attestation.getPayloadHash();
367
465
  return await this.store.transactionAsync(async ()=>{
368
- const key = this.getAttestationKey(slotNumber, proposalId, sender.toString());
369
- const alreadyExists = await this.checkpointAttestations.hasAsync(key);
370
- if (alreadyExists) {
371
- const total = await this.getAttestationCount(slotNumber, proposalId);
466
+ if (await this.multimapHasValue(this.attestationHashesPerSlotAndSigner, slotSignerKey, payloadHash)) {
467
+ const count = await this.attestationHashesPerSlotAndSigner.getValueCountAsync(slotSignerKey);
372
468
  return {
373
469
  added: false,
374
470
  alreadyExists: true,
375
- totalForPosition: total
471
+ count
376
472
  };
377
473
  }
378
- const limit = committeeSize + ATTESTATION_CAP_BUFFER;
379
- const currentCount = await this.getAttestationCount(slotNumber, proposalId);
380
- if (currentCount >= limit) {
474
+ const signerAttestationCount = await this.attestationHashesPerSlotAndSigner.getValueCountAsync(slotSignerKey);
475
+ if (signerAttestationCount >= MAX_ATTESTATIONS_PER_SLOT_AND_SIGNER) {
476
+ this.log.debug(`Rejecting attestation: signer ${signerAddress} exceeded per-slot cap for slot ${slotNumber}`, {
477
+ slotNumber,
478
+ signerAddress,
479
+ archive: attestation.archive.toString(),
480
+ payloadHash,
481
+ signerAttestationCount
482
+ });
381
483
  return {
382
484
  added: false,
383
485
  alreadyExists: false,
384
- totalForPosition: currentCount
486
+ count: signerAttestationCount
385
487
  };
386
488
  }
387
- await this.checkpointAttestations.set(key, attestation.toBuffer());
388
- this.log.debug(`Added checkpoint attestation for slot ${slotNumber} from ${sender.toString()}`, {
489
+ // Track the new payload hash for equivocation detection.
490
+ await this.attestationHashesPerSlotAndSigner.set(slotSignerKey, payloadHash);
491
+ // Only the first distinct payload at (slot, signer) is stored; later
492
+ // equivocations are detected via the multimap but their bytes are not retained.
493
+ const alreadyHasStored = await this.attestationPerSlotAndSigner.hasAsync(slotSignerKey);
494
+ if (!alreadyHasStored) {
495
+ // Store the signature in canonical (v ∈ {27, 28}) form. `sender` recovered above, so the
496
+ // signature is non-empty and safe to normalize; this keeps a yParity-encoded (v = 0/1) variant
497
+ // from a malicious peer or an in-flight byte mutation out of the L1 bundle downstream.
498
+ await this.attestationPerSlotAndSigner.set(slotSignerKey, attestation.withNormalizedSignature().toBuffer());
499
+ this.metrics.trackMempoolItemAdded(slotSignerKey);
500
+ }
501
+ this.log.debug(`Added checkpoint attestation for slot ${slotNumber} from ${signerAddress}`, {
389
502
  signature: attestation.signature.toString(),
390
503
  slotNumber,
391
- address: sender.toString(),
392
- proposalId
504
+ address: signerAddress,
505
+ archive: attestation.archive.toString(),
506
+ payloadHash,
507
+ stored: !alreadyHasStored
393
508
  });
394
509
  return {
395
510
  added: true,
396
511
  alreadyExists: false,
397
- totalForPosition: currentCount + 1
512
+ count: signerAttestationCount + 1
398
513
  };
399
514
  });
400
515
  }
401
- /** Gets the count of attestations for a given (slot, proposalId). */ async getAttestationCount(slot, proposalId) {
402
- const range = this.getAttestationKeyRangeForProposal(slot, proposalId);
403
- let count = 0;
404
- for await (const _ of this.checkpointAttestations.keysAsync(range)){
405
- count++;
406
- }
407
- return count;
408
- }
409
516
  }
410
517
  /** Creates an AttestationPool backed by a temporary store for testing. */ export async function createTestAttestationPool(telemetry) {
411
518
  const { openTmpStore } = await import('@aztec/kv-store/lmdb-v2');