@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,5 +1,4 @@
1
- import { IndexWithinCheckpoint, SlotNumber } from '@aztec/foundation/branded-types';
2
- import { Fr } from '@aztec/foundation/curves/bn254';
1
+ import type { BlockProposalHash, CheckpointProposalHash, SlotNumber } from '@aztec/foundation/branded-types';
3
2
  import { toArray } from '@aztec/foundation/iterable';
4
3
  import { createLogger } from '@aztec/foundation/log';
5
4
  import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncMultiMap } from '@aztec/kv-store';
@@ -15,30 +14,42 @@ import { PoolInstrumentation, PoolName, type PoolStatsCallback } from '../instru
15
14
 
16
15
  /** Result of trying to add an item (proposal or attestation) to the pool */
17
16
  export type TryAddResult = {
18
- /** Whether the item was added */
17
+ /** Whether the item was accepted into pool state. False when it already existed, was invalid, or hit a cap. */
19
18
  added: boolean;
20
- /** Whether the exact item already existed */
19
+ /** Whether the exact signed payload (matched by payload hash) already existed in the pool. */
21
20
  alreadyExists: boolean;
22
- /** Total items for this position - used for duplicate detection */
23
- totalForPosition: number;
21
+ /** Number of distinct signed-payload hashes seen for the position. Meaning varies by method:
22
+ * - tryAddBlockProposal: distinct payload hashes at (slot, indexWithinCheckpoint)
23
+ * - tryAddCheckpointProposal: distinct payload hashes at slot
24
+ * - tryAddCheckpointAttestation: distinct payload hashes by this signer for this slot */
25
+ count: number;
24
26
  };
25
27
 
26
- export const MAX_PROPOSALS_PER_SLOT = 5;
27
- export const MAX_PROPOSALS_PER_POSITION = 3;
28
- export const ATTESTATION_CAP_BUFFER = 10;
28
+ export type ProposalsForSlot = {
29
+ blockProposals: BlockProposal[];
30
+ checkpointProposals: CheckpointProposalCore[];
31
+ };
32
+
33
+ export const MAX_CHECKPOINT_PROPOSALS_PER_SLOT = 2;
34
+ export const MAX_BLOCK_PROPOSALS_PER_POSITION = 2;
35
+ /** Maximum attestations a single signer can make per slot before being rejected. */
36
+ export const MAX_ATTESTATIONS_PER_SLOT_AND_SIGNER = 2;
29
37
 
30
38
  /** Public API interface for attestation pools. Used for typing mocks and test implementations. */
31
39
  export type AttestationPoolApi = Pick<
32
40
  AttestationPool,
33
41
  | 'tryAddBlockProposal'
34
- | 'getBlockProposal'
42
+ | 'getBlockProposalByArchive'
43
+ | 'getProposalsForSlot'
35
44
  | 'tryAddCheckpointProposal'
36
45
  | 'getCheckpointProposal'
46
+ | 'hasCheckpointProposalForSlot'
37
47
  | 'addOwnCheckpointAttestations'
38
48
  | 'tryAddCheckpointAttestation'
39
49
  | 'deleteOlderThan'
40
50
  | 'getCheckpointAttestationsForSlot'
41
51
  | 'getCheckpointAttestationsForSlotAndProposal'
52
+ | 'hasBlockProposalsForSlot'
42
53
  | 'isEmpty'
43
54
  >;
44
55
 
@@ -47,27 +58,45 @@ export type AttestationPoolApi = Pick<
47
58
  *
48
59
  * Attestations and proposals observed via the p2p network are stored for requests
49
60
  * from the validator to produce a block, or to serve to other peers.
61
+ *
62
+ * Equivocation detection: distinct *signed payload hashes* arriving at the same
63
+ * position are tracked in the matching index multimap so the equivocation count
64
+ * reaches 2 even when archive collides on `feeAssetPriceModifier` variants.
65
+ * Proposal bytes are retained per accepted payload hash, up to the same equivocation
66
+ * caps, for slashing watchers that need signed P2P proposals.
50
67
  */
51
68
  export class AttestationPool {
52
69
  private metrics: PoolInstrumentation<CheckpointAttestation>;
53
70
 
54
- // Checkpoint attestations from attestation key (slot-proposalId-signer) to serialized CheckpointAttestation
55
- // Keys are lexicographically sortable allowing range queries by slot or by (slot, proposalId)
56
- private checkpointAttestations: AztecAsyncMap<string, Buffer>;
71
+ // Checkpoint attestations from `${paddedSlot}-${signer}` to serialized CheckpointAttestation.
72
+ // Stores the first attestation seen per (slot, signer); subsequent distinct payload
73
+ // hashes from the same signer are tracked only in `attestationHashesPerSlotAndSigner`
74
+ // for equivocation detection.
75
+ private attestationPerSlotAndSigner: AztecAsyncMap<string, Buffer>;
57
76
 
58
- // Checkpoint proposals from proposal archive to serialized CheckpointProposal
59
- private checkpointProposals: AztecAsyncMap<string, Buffer>;
77
+ // Distinct payload hashes seen per (slot, signer) for tracking attestation equivocations.
78
+ // Key: `${paddedSlot}-${signerAddress}`, Value: CheckpointProposalHash (`0x`-prefixed hex)
79
+ private attestationHashesPerSlotAndSigner: AztecAsyncMultiMap<string, CheckpointProposalHash>;
60
80
 
61
- // Checkpoint proposals indexed by slot for querying all proposals in a slot
62
- // Key: slot number, Value: proposal archive strings
63
- private checkpointProposalsForSlot: AztecAsyncMultiMap<number, string>;
81
+ // Checkpoint proposals from `${paddedSlot}-${payloadHash}` to serialized CheckpointProposalCore.
82
+ // Stores every accepted distinct payload up to MAX_CHECKPOINT_PROPOSALS_PER_SLOT.
83
+ private checkpointProposalsPerSlotAndHash: AztecAsyncMap<string, Buffer>;
64
84
 
65
- // Block proposals from proposal archive to serialized BlockProposal
66
- private blockProposals: AztecAsyncMap<string, Buffer>;
85
+ // Distinct payload hashes seen per slot. Hash collision = duplicate.
86
+ // Hash count reaching 2 = equivocation.
87
+ // Key: slot number, Value: CheckpointProposalHash (`0x`-prefixed hex)
88
+ private checkpointProposalHashesPerSlot: AztecAsyncMultiMap<number, CheckpointProposalHash>;
67
89
 
68
- // Block proposals indexed by slot and index-within-checkpoint for duplicate detection
69
- // Key: (slot << 10) | indexWithinCheckpoint, Value: archive string
70
- private blockProposalsForSlotAndIndex: AztecAsyncMultiMap<number, string>;
90
+ // Block proposals from `${paddedSlot}-${paddedIndex}-${payloadHash}` to serialized BlockProposal.
91
+ // Stores every accepted distinct payload up to MAX_BLOCK_PROPOSALS_PER_POSITION.
92
+ private blockProposalsPerSlotIndexAndHash: AztecAsyncMap<string, Buffer>;
93
+
94
+ // Distinct payload hashes seen per (slot, indexWithinCheckpoint).
95
+ // Key: slot * (1 << INDEX_BITS) + indexWithinCheckpoint, Value: BlockProposalHash (`0x`-prefixed hex)
96
+ private blockProposalHashesPerSlotAndIndex: AztecAsyncMultiMap<number, BlockProposalHash>;
97
+
98
+ // Secondary index from archive root to all retained block proposal keys.
99
+ private blockProposalKeysPerArchive: AztecAsyncMultiMap<string, string>;
71
100
 
72
101
  constructor(
73
102
  private store: AztecAsyncKVStore,
@@ -75,177 +104,239 @@ export class AttestationPool {
75
104
  private log = createLogger('aztec:attestation_pool'),
76
105
  ) {
77
106
  // Initialize block proposal storage
78
- this.blockProposals = store.openMap('proposals');
79
- this.blockProposalsForSlotAndIndex = store.openMultiMap('block_proposals_for_slot_and_index');
107
+ this.blockProposalsPerSlotIndexAndHash = store.openMap('block_proposals_by_slot_index_and_hash');
108
+ this.blockProposalHashesPerSlotAndIndex = store.openMultiMap('block_proposals_for_slot_and_index');
109
+ this.blockProposalKeysPerArchive = store.openMultiMap('block_proposals_by_archive');
80
110
 
81
111
  // Initialize checkpoint attestations storage
82
- this.checkpointAttestations = store.openMap('checkpoint_attestations');
112
+ this.attestationPerSlotAndSigner = store.openMap('checkpoint_attestations');
113
+ this.attestationHashesPerSlotAndSigner = store.openMultiMap('checkpoint_attestations_per_slot_and_signer');
83
114
 
84
115
  // Initialize checkpoint proposal storage
85
- this.checkpointProposals = store.openMap('checkpoint_proposals');
86
- this.checkpointProposalsForSlot = store.openMultiMap('checkpoint_proposals_for_slot');
116
+ this.checkpointProposalsPerSlotAndHash = store.openMap('checkpoint_proposals_by_slot_and_hash');
117
+ this.checkpointProposalHashesPerSlot = store.openMultiMap('checkpoint_proposals_for_slot');
87
118
 
88
119
  this.metrics = new PoolInstrumentation(telemetry, PoolName.ATTESTATION_POOL, this.poolStats);
89
120
  }
90
121
 
91
122
  private poolStats: PoolStatsCallback = async () => {
92
123
  return {
93
- itemCount: await this.checkpointAttestations.sizeAsync(),
124
+ itemCount: await this.attestationPerSlotAndSigner.sizeAsync(),
94
125
  };
95
126
  };
96
127
 
97
128
  /** Returns whether the pool is empty. */
98
129
  public async isEmpty(): Promise<boolean> {
99
- for await (const _ of this.checkpointAttestations.entriesAsync()) {
100
- return false;
101
- }
102
- for await (const _ of this.blockProposals.entriesAsync()) {
103
- return false;
104
- }
105
- return true;
130
+ const [attestationCount, blockProposalCount, checkpointProposalCount] = await Promise.all([
131
+ this.attestationPerSlotAndSigner.sizeAsync(),
132
+ this.blockProposalsPerSlotIndexAndHash.sizeAsync(),
133
+ this.checkpointProposalsPerSlotAndHash.sizeAsync(),
134
+ ]);
135
+
136
+ return attestationCount === 0 && blockProposalCount === 0 && checkpointProposalCount === 0;
137
+ }
138
+
139
+ /** Number of bits reserved for indexWithinCheckpoint in position keys. */
140
+ private static readonly INDEX_BITS = 10;
141
+ /** Maximum indexWithinCheckpoint value (2^10 - 1 = 1023). */
142
+ private static readonly MAX_INDEX = (1 << AttestationPool.INDEX_BITS) - 1;
143
+ /** Decimal digits used to left-pad slot numbers in string keys.
144
+ * 10 digits ≈ 3500 years at 36 s/slot, leaving ample headroom. */
145
+ private static readonly SLOT_PAD_DIGITS = 10;
146
+
147
+ /** Fixed-width decimal slot string for use in composite string keys. */
148
+ private slotPaddedKey(slot: SlotNumber | number): string {
149
+ return slot.toString().padStart(AttestationPool.SLOT_PAD_DIGITS, '0');
106
150
  }
107
151
 
108
- private getProposalKey(slot: number | bigint | Fr | string, proposalId: Fr | string | Buffer): string {
109
- const slotStr = typeof slot === 'string' ? slot : new Fr(slot).toString();
110
- const proposalIdStr =
111
- typeof proposalId === 'string'
112
- ? proposalId
113
- : Buffer.isBuffer(proposalId)
114
- ? Fr.fromBuffer(proposalId).toString()
115
- : proposalId.toString();
152
+ /** Fixed-width decimal index string for use in composite string keys. */
153
+ private indexPaddedKey(indexWithinCheckpoint: number): string {
154
+ return indexWithinCheckpoint.toString().padStart(4, '0');
155
+ }
116
156
 
117
- return `${slotStr}-${proposalIdStr}`;
157
+ /** Key for retained block proposals. */
158
+ private getBlockProposalKey(
159
+ slot: SlotNumber | number,
160
+ indexWithinCheckpoint: number,
161
+ payloadHash: BlockProposalHash,
162
+ ): string {
163
+ return `${this.slotPaddedKey(slot)}-${this.indexPaddedKey(indexWithinCheckpoint)}-${payloadHash}`;
118
164
  }
119
165
 
120
- private getAttestationKey(slot: number | bigint | Fr | string, proposalId: Fr | string, address: string): string {
121
- return `${this.getProposalKey(slot, proposalId)}-${address}`;
166
+ /** Range bounds for all retained block proposals in a slot. */
167
+ private getBlockProposalKeyRangeForSlot(slot: SlotNumber): { start: string; end: string } {
168
+ return { start: `${this.slotPaddedKey(slot)}-`, end: `${this.slotPaddedKey(slot + 1)}-` };
122
169
  }
123
170
 
124
- /** Returns range bounds for querying all attestations for a given slot. */
125
- private getAttestationKeyRangeForSlot(slot: SlotNumber): { start: string; end: string } {
126
- const slotStr = new Fr(slot).toString();
127
- return { start: `${slotStr}-`, end: `${slotStr}-Z` }; // 'Z' sorts after any hex character
171
+ /** Key for retained checkpoint proposals. */
172
+ private getCheckpointProposalKey(slot: SlotNumber | number, payloadHash: CheckpointProposalHash): string {
173
+ return `${this.slotPaddedKey(slot)}-${payloadHash}`;
128
174
  }
129
175
 
130
- /** Returns range bounds for querying all attestations for a given (slot, proposalId). */
131
- private getAttestationKeyRangeForProposal(slot: SlotNumber, proposalId: string): { start: string; end: string } {
132
- const proposalKey = this.getProposalKey(slot, proposalId);
133
- return { start: `${proposalKey}-`, end: `${proposalKey}-Z` };
176
+ /** Range bounds for all retained checkpoint proposals in a slot. */
177
+ private getCheckpointProposalKeyRangeForSlot(slot: SlotNumber): { start: string; end: string } {
178
+ return { start: `${this.slotPaddedKey(slot)}-`, end: `${this.slotPaddedKey(slot + 1)}-` };
134
179
  }
135
180
 
136
- /** Number of bits reserved for indexWithinCheckpoint in position keys. */
137
- private static readonly INDEX_BITS = 10;
138
- /** Maximum indexWithinCheckpoint value (2^10 - 1 = 1023). */
139
- private static readonly MAX_INDEX = (1 << AttestationPool.INDEX_BITS) - 1;
181
+ /** Key for the per-(slot, signer) attestation main store and equivocation index. */
182
+ private getSlotSignerKey(slot: SlotNumber, signerAddress: string): string {
183
+ return `${this.slotPaddedKey(slot)}-${signerAddress}`;
184
+ }
140
185
 
141
- /** Creates a position key for block proposals: (slot << 10) | indexWithinCheckpoint. */
186
+ /**
187
+ * Returns range bounds for querying all attestations for a given slot.
188
+ * Fixed-width padding ensures the slot prefix sorts cleanly, so using the next
189
+ * slot's prefix as the upper bound captures exactly the current slot's entries.
190
+ */
191
+ private getAttestationKeyRangeForSlot(slot: SlotNumber): { start: string; end: string } {
192
+ return { start: `${this.slotPaddedKey(slot)}-`, end: `${this.slotPaddedKey(slot + 1)}-` };
193
+ }
194
+
195
+ /** Creates a position key for block proposals: slot * 1024 + indexWithinCheckpoint.
196
+ * Uses multiplication instead of bit-shift to avoid 32-bit signed integer overflow
197
+ * (bit-shift overflows after slot ~2^21, roughly 278 days of uptime). */
142
198
  private getBlockPositionKey(slot: number, indexWithinCheckpoint: number): number {
143
199
  if (indexWithinCheckpoint > AttestationPool.MAX_INDEX) {
144
200
  throw new Error(
145
201
  `Value for indexWithinCheckpoint ${indexWithinCheckpoint} exceeds maximum ${AttestationPool.MAX_INDEX}`,
146
202
  );
147
203
  }
148
- return (slot << AttestationPool.INDEX_BITS) | indexWithinCheckpoint;
204
+ return slot * (1 << AttestationPool.INDEX_BITS) + indexWithinCheckpoint;
205
+ }
206
+
207
+ /** Returns true if the multimap already contains the given value for the given key. */
208
+ private async multimapHasValue<TKey extends number | string, TValue extends string>(
209
+ map: AztecAsyncMultiMap<TKey, TValue>,
210
+ key: TKey,
211
+ value: TValue,
212
+ ): Promise<boolean> {
213
+ const values = await toArray(map.getValuesAsync(key));
214
+ return values.includes(value);
149
215
  }
150
216
 
151
217
  /**
152
218
  * Attempts to add a block proposal to the pool.
153
219
  *
154
- * This method performs validation and addition in a single call:
155
- * - Checks if the proposal already exists (returns alreadyExists: true if so)
156
- * - Checks if the position has reached the proposal cap (returns added: false if so)
157
- * - Adds the proposal if validation passes
220
+ * - Detects duplicates by signed-payload hash (not archive); a re-broadcast of the
221
+ * exact same signed payload returns `alreadyExists: true`.
222
+ * - Distinct payload hashes at the same `(slot, indexWithinCheckpoint)` are tracked
223
+ * in the equivocation index and retained up to the cap.
158
224
  *
159
225
  * @param blockProposal - The block proposal to add
160
226
  * @returns Result indicating whether the proposal was added and duplicate detection info
161
227
  */
162
228
  public async tryAddBlockProposal(blockProposal: BlockProposal): Promise<TryAddResult> {
163
229
  return await this.store.transactionAsync(async () => {
164
- const proposalId = blockProposal.archive.toString();
165
-
166
- // Check if already exists
167
- const alreadyExists = await this.blockProposals.hasAsync(proposalId);
168
- if (alreadyExists) {
169
- const totalForPosition = await this.getBlockProposalCountForPosition(
170
- blockProposal.slotNumber,
171
- blockProposal.indexWithinCheckpoint,
172
- );
173
- return { added: false, alreadyExists: true, totalForPosition };
230
+ const positionKey = this.getBlockPositionKey(blockProposal.slotNumber, blockProposal.indexWithinCheckpoint);
231
+ const payloadHash = blockProposal.getPayloadHash();
232
+
233
+ // Hash already tracked => exact same signed payload was already received.
234
+ if (await this.multimapHasValue(this.blockProposalHashesPerSlotAndIndex, positionKey, payloadHash)) {
235
+ const count = await this.blockProposalHashesPerSlotAndIndex.getValueCountAsync(positionKey);
236
+ return { added: false, alreadyExists: true, count };
174
237
  }
175
238
 
176
- // Get current count for position and check cap, do not add if exceeded
177
- const totalForPosition = await this.getBlockProposalCountForPosition(
239
+ // Cap reached for this position (no more new payload hashes accepted).
240
+ const count = await this.blockProposalHashesPerSlotAndIndex.getValueCountAsync(positionKey);
241
+ if (count >= MAX_BLOCK_PROPOSALS_PER_POSITION) {
242
+ return { added: false, alreadyExists: false, count };
243
+ }
244
+
245
+ // Track the new payload hash for equivocation detection.
246
+ await this.blockProposalHashesPerSlotAndIndex.set(positionKey, payloadHash);
247
+ const proposalKey = this.getBlockProposalKey(
178
248
  blockProposal.slotNumber,
179
249
  blockProposal.indexWithinCheckpoint,
250
+ payloadHash,
180
251
  );
181
-
182
- if (totalForPosition >= MAX_PROPOSALS_PER_POSITION) {
183
- return { added: false, alreadyExists: false, totalForPosition };
184
- }
185
-
186
- // Add the proposal
187
- await this.addBlockProposal(blockProposal);
252
+ await this.blockProposalsPerSlotIndexAndHash.set(proposalKey, blockProposal.withoutSignedTxs().toBuffer());
253
+ await this.blockProposalKeysPerArchive.set(blockProposal.archive.toString(), proposalKey);
188
254
 
189
255
  this.log.debug(
190
256
  `Added block proposal for slot ${blockProposal.slotNumber} and index ${blockProposal.indexWithinCheckpoint}`,
191
257
  {
192
- proposalId,
258
+ archive: blockProposal.archive.toString(),
259
+ payloadHash,
193
260
  slotNumber: blockProposal.slotNumber,
194
261
  indexWithinCheckpoint: blockProposal.indexWithinCheckpoint,
195
262
  },
196
263
  );
197
264
 
198
- return { added: true, alreadyExists: false, totalForPosition: totalForPosition + 1 };
265
+ return { added: true, alreadyExists: false, count: count + 1 };
199
266
  });
200
267
  }
201
268
 
202
- /** Gets the count of block proposals for a given position (slot, indexWithinCheckpoint). */
203
- private getBlockProposalCountForPosition(
204
- slot: SlotNumber,
205
- indexWithinCheckpoint: IndexWithinCheckpoint,
206
- ): Promise<number> {
207
- const positionKey = this.getBlockPositionKey(slot, indexWithinCheckpoint);
208
- return this.blockProposalsForSlotAndIndex.getValueCountAsync(positionKey);
209
- }
210
-
211
- /** Internal method - must be called within a transaction. */
212
- private async addBlockProposal(blockProposal: BlockProposal): Promise<void> {
213
- const proposalId = blockProposal.archive.toString();
214
- // Strip signedTxs before storing to avoid persisting full tx data
215
- await this.blockProposals.set(proposalId, blockProposal.withoutSignedTxs().toBuffer());
216
-
217
- // Index by slot and position for duplicate detection
218
- const positionKey = this.getBlockPositionKey(blockProposal.slotNumber, blockProposal.indexWithinCheckpoint);
219
- await this.blockProposalsForSlotAndIndex.set(positionKey, proposalId);
220
- }
221
-
222
269
  /**
223
- * Get block proposal by its ID.
270
+ * Get block proposal by archive root.
224
271
  *
225
- * @param id - The ID of the block proposal to retrieve. The ID is proposal.payload.archive
272
+ * Resolves the archive root through the archive index and returns the first
273
+ * retained proposal for that archive. This lookup is used by block-txs req/resp,
274
+ * where any retained proposal for the requested archive gives the tx hash list.
226
275
  *
227
- * @return The block proposal if it exists, otherwise undefined.
276
+ * @param archiveRoot - The archive root to look up
277
+ * @return The block proposal if it exists and its archive matches, otherwise undefined.
228
278
  */
229
- public async getBlockProposal(id: string): Promise<BlockProposal | undefined> {
230
- const buffer = await this.blockProposals.getAsync(id);
231
- try {
232
- if (buffer && buffer.length > 0) {
233
- return BlockProposal.fromBuffer(buffer);
279
+ public async getBlockProposalByArchive(archiveRoot: string): Promise<BlockProposal | undefined> {
280
+ for await (const proposalKey of this.blockProposalKeysPerArchive.getValuesAsync(archiveRoot)) {
281
+ const buffer = await this.blockProposalsPerSlotIndexAndHash.getAsync(proposalKey);
282
+ if (!buffer || buffer.length === 0) {
283
+ continue;
284
+ }
285
+ try {
286
+ const proposal = BlockProposal.fromBuffer(buffer);
287
+ if (proposal.archive.toString() === archiveRoot) {
288
+ return proposal;
289
+ }
290
+ } catch {
291
+ continue;
234
292
  }
235
- } catch {
236
- return undefined;
237
293
  }
238
-
239
294
  return undefined;
240
295
  }
241
296
 
297
+ /** Returns retained signed proposals for a slot. */
298
+ public async getProposalsForSlot(slot: SlotNumber): Promise<ProposalsForSlot> {
299
+ const blockProposals: BlockProposal[] = [];
300
+ const checkpointProposals: CheckpointProposalCore[] = [];
301
+
302
+ for await (const [_, buffer] of this.blockProposalsPerSlotIndexAndHash.entriesAsync(
303
+ this.getBlockProposalKeyRangeForSlot(slot),
304
+ )) {
305
+ try {
306
+ blockProposals.push(BlockProposal.fromBuffer(buffer));
307
+ } catch {
308
+ continue;
309
+ }
310
+ }
311
+
312
+ for await (const [_, buffer] of this.checkpointProposalsPerSlotAndHash.entriesAsync(
313
+ this.getCheckpointProposalKeyRangeForSlot(slot),
314
+ )) {
315
+ try {
316
+ checkpointProposals.push(CheckpointProposal.fromBuffer(buffer));
317
+ } catch {
318
+ continue;
319
+ }
320
+ }
321
+
322
+ return { blockProposals, checkpointProposals };
323
+ }
324
+
325
+ /** Checks if any block proposals exist for a given slot (at index 0). */
326
+ public async hasBlockProposalsForSlot(slot: SlotNumber): Promise<boolean> {
327
+ const positionKey = this.getBlockPositionKey(slot, 0);
328
+ const count = await this.blockProposalHashesPerSlotAndIndex.getValueCountAsync(positionKey);
329
+ return count > 0;
330
+ }
331
+
242
332
  /**
243
333
  * Attempts to add a checkpoint proposal to the pool.
244
334
  *
245
- * This method performs validation and addition in a single call:
246
- * - Checks if the proposal already exists (returns alreadyExists: true if so)
247
- * - Checks if the slot has reached the proposal cap (returns added: false if so)
248
- * - Adds the proposal if validation passes
335
+ * - Detects duplicates by signed-payload hash (not archive); a re-broadcast of the
336
+ * exact same signed payload returns `alreadyExists: true`.
337
+ * - Distinct payload hashes at the same slot are tracked in the equivocation index.
338
+ * Distinct payload bytes are retained up to the same cap so slashing watchers
339
+ * can recover signed proposals.
249
340
  *
250
341
  * Note: This method only handles the CheckpointProposalCore. If the original
251
342
  * CheckpointProposal contains a lastBlock, the caller should extract it via
@@ -256,73 +347,77 @@ export class AttestationPool {
256
347
  */
257
348
  public async tryAddCheckpointProposal(proposal: CheckpointProposalCore): Promise<TryAddResult> {
258
349
  return await this.store.transactionAsync(async () => {
259
- const proposalId = proposal.archive.toString();
350
+ const slot = proposal.slotNumber;
351
+ const payloadHash = proposal.getPayloadHash();
260
352
 
261
- // Check if already exists
262
- const alreadyExists = await this.checkpointProposals.hasAsync(proposalId);
263
- if (alreadyExists) {
264
- const totalForPosition = await this.checkpointProposalsForSlot.getValueCountAsync(proposal.slotNumber);
265
- return { added: false, alreadyExists: true, totalForPosition };
353
+ if (await this.multimapHasValue(this.checkpointProposalHashesPerSlot, slot, payloadHash)) {
354
+ const count = await this.checkpointProposalHashesPerSlot.getValueCountAsync(slot);
355
+ return { added: false, alreadyExists: true, count };
266
356
  }
267
357
 
268
- // Get current count for slot and check cap
269
- const totalForPosition = await this.checkpointProposalsForSlot.getValueCountAsync(proposal.slotNumber);
270
- if (totalForPosition >= MAX_PROPOSALS_PER_SLOT) {
271
- return { added: false, alreadyExists: false, totalForPosition };
358
+ const count = await this.checkpointProposalHashesPerSlot.getValueCountAsync(slot);
359
+ if (count >= MAX_CHECKPOINT_PROPOSALS_PER_SLOT) {
360
+ return { added: false, alreadyExists: false, count };
272
361
  }
273
362
 
274
- // Add the proposal if cap not exceeded
275
- await this.addCheckpointProposal(proposal);
363
+ // Track the new payload hash for equivocation detection.
364
+ await this.checkpointProposalHashesPerSlot.set(slot, payloadHash);
365
+ await this.checkpointProposalsPerSlotAndHash.set(
366
+ this.getCheckpointProposalKey(slot, payloadHash),
367
+ proposal.toBuffer(),
368
+ );
276
369
 
277
- this.log.debug(`Added checkpoint proposal for slot ${proposal.slotNumber}`, {
278
- proposalId,
279
- slotNumber: proposal.slotNumber,
370
+ this.log.debug(`Added checkpoint proposal for slot ${slot}`, {
371
+ archive: proposal.archive.toString(),
372
+ payloadHash,
373
+ slotNumber: slot,
280
374
  });
281
375
 
282
- return { added: true, alreadyExists: false, totalForPosition: totalForPosition + 1 };
376
+ return { added: true, alreadyExists: false, count: count + 1 };
283
377
  });
284
378
  }
285
379
 
286
- /** Internal method - must be called within a transaction. */
287
- private async addCheckpointProposal(proposal: CheckpointProposalCore): Promise<void> {
288
- const slotKey = proposal.slotNumber;
289
- const proposalId = proposal.archive.toString();
290
-
291
- await this.checkpointProposalsForSlot.set(slotKey, proposalId);
292
- await this.checkpointProposals.set(proposalId, proposal.toBuffer());
293
- }
294
-
295
380
  /**
296
- * Get checkpoint proposal by its ID.
381
+ * Get a retained checkpoint proposal stored for the given slot.
382
+ * If multiple proposals were retained for an equivocation, returns the lowest
383
+ * payload hash deterministically.
297
384
  *
298
385
  * Returns a CheckpointProposalCore (without lastBlock info) since the lastBlock
299
386
  * is extracted and stored separately as a BlockProposal when added.
300
387
  *
301
- * @param id - The ID of the checkpoint proposal to retrieve (proposal.archive)
302
- * @return The checkpoint proposal core if it exists, otherwise undefined.
388
+ * @param slot - The slot to look up
389
+ * @return The checkpoint proposal core if one is stored, otherwise undefined.
303
390
  */
304
- public async getCheckpointProposal(id: string): Promise<CheckpointProposalCore | undefined> {
305
- const buffer = await this.checkpointProposals.getAsync(id);
306
- try {
307
- if (buffer && buffer.length > 0) {
308
- return CheckpointProposal.fromBuffer(buffer);
391
+ public async getCheckpointProposal(slot: SlotNumber): Promise<CheckpointProposalCore | undefined> {
392
+ for await (const [_, buffer] of this.checkpointProposalsPerSlotAndHash.entriesAsync(
393
+ this.getCheckpointProposalKeyRangeForSlot(slot),
394
+ )) {
395
+ try {
396
+ if (buffer && buffer.length > 0) {
397
+ return CheckpointProposal.fromBuffer(buffer);
398
+ }
399
+ } catch {
400
+ continue;
309
401
  }
310
- } catch {
311
- return undefined;
312
402
  }
313
403
 
314
404
  return undefined;
315
405
  }
316
406
 
407
+ /** Returns whether any checkpoint proposal is retained for the given slot. */
408
+ public async hasCheckpointProposalForSlot(slot: SlotNumber): Promise<boolean> {
409
+ return (await this.getCheckpointProposal(slot)) !== undefined;
410
+ }
411
+
317
412
  /**
318
413
  * Adds own checkpoint attestations to the pool.
319
- * Skips validations on number of checkpoint attestations stored for the given slot.
414
+ * Skips per-signer cap and equivocation tracking; the caller is trusted.
415
+ * Each (slot, signer) gets a single stored attestation; later additions overwrite.
320
416
  */
321
417
  public async addOwnCheckpointAttestations(attestations: CheckpointAttestation[]): Promise<void> {
322
418
  await this.store.transactionAsync(async () => {
323
419
  for (const attestation of attestations) {
324
420
  const slotNumber = attestation.payload.header.slotNumber;
325
- const proposalId = attestation.archive;
326
421
  const sender = attestation.getSender();
327
422
 
328
423
  // Skip attestations with invalid signatures
@@ -330,23 +425,31 @@ export class AttestationPool {
330
425
  this.log.warn(`Skipping own checkpoint attestation with invalid signature for slot ${slotNumber}`, {
331
426
  signature: attestation.signature.toString(),
332
427
  slotNumber,
333
- proposalId,
428
+ archive: attestation.archive.toString(),
334
429
  });
335
430
  continue;
336
431
  }
337
432
 
338
433
  const address = sender.toString();
434
+ const ownKey = this.getSlotSignerKey(slotNumber, address);
435
+ const payloadHash = attestation.getPayloadHash();
339
436
 
340
- await this.checkpointAttestations.set(
341
- this.getAttestationKey(slotNumber, proposalId, address),
342
- attestation.toBuffer(),
343
- );
437
+ // Store the signature in canonical (v ∈ {27, 28}) form; see tryAddCheckpointAttestation.
438
+ await this.attestationPerSlotAndSigner.set(ownKey, attestation.withNormalizedSignature().toBuffer());
439
+ this.metrics.trackMempoolItemAdded(ownKey);
440
+
441
+ // Track our own payload hash so that an equivocating attestation from another
442
+ // peer at the same (slot, signer) is detected as a duplicate.
443
+ if (!(await this.multimapHasValue(this.attestationHashesPerSlotAndSigner, ownKey, payloadHash))) {
444
+ await this.attestationHashesPerSlotAndSigner.set(ownKey, payloadHash);
445
+ }
344
446
 
345
447
  this.log.debug(`Added own checkpoint attestation for slot ${slotNumber} from ${address}`, {
346
448
  signature: attestation.signature.toString(),
347
449
  slotNumber,
348
450
  address,
349
- proposalId,
451
+ archive: attestation.archive.toString(),
452
+ payloadHash,
350
453
  });
351
454
  }
352
455
  });
@@ -355,6 +458,10 @@ export class AttestationPool {
355
458
  /**
356
459
  * Get all checkpoint attestations for a given slot.
357
460
  *
461
+ * Returns one attestation per (slot, signer) — the first seen for each signer.
462
+ * Later equivocating attestations from the same signer are tracked in the index
463
+ * but their bytes are not retained.
464
+ *
358
465
  * @param slot - The slot to query
359
466
  * @return CheckpointAttestations
360
467
  */
@@ -362,7 +469,7 @@ export class AttestationPool {
362
469
  const range = this.getAttestationKeyRangeForSlot(slot);
363
470
  const attestations: CheckpointAttestation[] = [];
364
471
 
365
- for await (const [_, buf] of this.checkpointAttestations.entriesAsync(range)) {
472
+ for await (const [_, buf] of this.attestationPerSlotAndSigner.entriesAsync(range)) {
366
473
  attestations.push(CheckpointAttestation.fromBuffer(buf));
367
474
  }
368
475
 
@@ -370,24 +477,19 @@ export class AttestationPool {
370
477
  }
371
478
 
372
479
  /**
373
- * Get checkpoint attestations for slot and given proposal.
480
+ * Get checkpoint attestations for a slot whose signed payload matches the given
481
+ * proposal payload hash.
374
482
  *
375
483
  * @param slot - The slot to query
376
- * @param proposalId - The proposal to query
377
- * @return CheckpointAttestations
484
+ * @param proposalPayloadHash - Hex-encoded keccak256 of the target proposal's signed payload
485
+ * @return CheckpointAttestations whose `getPayloadHash()` matches `proposalPayloadHash`
378
486
  */
379
487
  public async getCheckpointAttestationsForSlotAndProposal(
380
488
  slot: SlotNumber,
381
- proposalId: string,
489
+ proposalPayloadHash: CheckpointProposalHash,
382
490
  ): Promise<CheckpointAttestation[]> {
383
- const range = this.getAttestationKeyRangeForProposal(slot, proposalId);
384
- const attestations: CheckpointAttestation[] = [];
385
-
386
- for await (const [_, buf] of this.checkpointAttestations.entriesAsync(range)) {
387
- attestations.push(CheckpointAttestation.fromBuffer(buf));
388
- }
389
-
390
- return attestations;
491
+ const all = await this.getCheckpointAttestationsForSlot(slot);
492
+ return all.filter(att => att.getPayloadHash() === proposalPayloadHash);
391
493
  }
392
494
 
393
495
  /**
@@ -401,34 +503,51 @@ export class AttestationPool {
401
503
  let numberOfBlockProposals = 0;
402
504
 
403
505
  await this.store.transactionAsync(async () => {
404
- // Delete checkpoint attestations with slot < oldestSlot
405
- // Attestation keys start with Fr(slot).toString(), so we use end bound of Fr(oldestSlot).toString()
406
- const attestationEndKey = new Fr(oldestSlot).toString();
407
- for await (const key of this.checkpointAttestations.keysAsync({ end: attestationEndKey })) {
408
- await this.checkpointAttestations.delete(key);
506
+ const oldestSlotPadded = this.slotPaddedKey(oldestSlot);
507
+
508
+ // Delete checkpoint attestations whose key < `${oldestSlotPadded}-`. Fixed-width
509
+ // decimal padding means the slot prefix sorts strictly before any key at that slot.
510
+ for await (const key of this.attestationPerSlotAndSigner.keysAsync({ end: `${oldestSlotPadded}-` })) {
511
+ await this.attestationPerSlotAndSigner.delete(key);
512
+ this.metrics.trackMempoolItemRemoved(key);
409
513
  numberOfAttestations++;
410
514
  }
411
515
 
412
- // Delete checkpoint proposals for slots < oldestSlot, using checkpointProposalsForSlot as index
413
- for await (const slot of this.checkpointProposalsForSlot.keysAsync({ end: oldestSlot })) {
414
- const proposalIds = await toArray(this.checkpointProposalsForSlot.getValuesAsync(slot));
415
- for (const proposalId of proposalIds) {
416
- await this.checkpointProposals.delete(proposalId);
417
- numberOfCheckpointProposals++;
418
- }
419
- await this.checkpointProposalsForSlot.delete(slot);
516
+ // Clean up per-signer-per-slot index using the same end bound.
517
+ for await (const key of this.attestationHashesPerSlotAndSigner.keysAsync({ end: `${oldestSlotPadded}-` })) {
518
+ await this.attestationHashesPerSlotAndSigner.delete(key);
519
+ }
520
+
521
+ // Delete checkpoint proposals for slots < oldestSlot.
522
+ for await (const slot of this.checkpointProposalHashesPerSlot.keysAsync({ end: oldestSlot })) {
523
+ await this.checkpointProposalHashesPerSlot.delete(slot);
524
+ }
525
+
526
+ for await (const key of this.checkpointProposalsPerSlotAndHash.keysAsync({
527
+ end: `${oldestSlotPadded}-`,
528
+ })) {
529
+ await this.checkpointProposalsPerSlotAndHash.delete(key);
530
+ numberOfCheckpointProposals++;
420
531
  }
421
532
 
422
- // Delete block proposals for slots < oldestSlot, using blockProposalsForSlotAndIndex as index
423
- // Key format: (slot << INDEX_BITS) | indexWithinCheckpoint
424
- const blockPositionEndKey = oldestSlot << AttestationPool.INDEX_BITS;
425
- for await (const positionKey of this.blockProposalsForSlotAndIndex.keysAsync({ end: blockPositionEndKey })) {
426
- const proposalIds = await toArray(this.blockProposalsForSlotAndIndex.getValuesAsync(positionKey));
427
- for (const proposalId of proposalIds) {
428
- await this.blockProposals.delete(proposalId);
429
- numberOfBlockProposals++;
533
+ // Delete block proposals for slots < oldestSlot, using blockProposalHashesPerSlotAndIndex as index.
534
+ // Key format: slot * (1 << INDEX_BITS) + indexWithinCheckpoint
535
+ const blockPositionEndKey = oldestSlot * (1 << AttestationPool.INDEX_BITS);
536
+ for await (const positionKey of this.blockProposalHashesPerSlotAndIndex.keysAsync({ end: blockPositionEndKey })) {
537
+ await this.blockProposalHashesPerSlotAndIndex.delete(positionKey);
538
+ }
539
+
540
+ for await (const [key, buffer] of this.blockProposalsPerSlotIndexAndHash.entriesAsync({
541
+ end: `${oldestSlotPadded}-`,
542
+ })) {
543
+ try {
544
+ const proposal = BlockProposal.fromBuffer(buffer);
545
+ await this.blockProposalKeysPerArchive.deleteValue(proposal.archive.toString(), key);
546
+ } catch {
547
+ // ignore decode errors when cleaning up
430
548
  }
431
- await this.blockProposalsForSlotAndIndex.delete(positionKey);
549
+ await this.blockProposalsPerSlotIndexAndHash.delete(key);
550
+ numberOfBlockProposals++;
432
551
  }
433
552
  });
434
553
 
@@ -443,63 +562,81 @@ export class AttestationPool {
443
562
  /**
444
563
  * Attempts to add a checkpoint attestation to the pool.
445
564
  *
446
- * This method performs validation and addition in a single call:
447
- * - Checks if the attestation already exists (returns alreadyExists: true if so)
448
- * - Checks if the (slot, proposalId) has reached the attestation cap (returns added: false if so)
449
- * - Adds the attestation if validation passes
565
+ * - Detects duplicates by signed-payload hash (not archive); a re-broadcast of the
566
+ * exact same signed payload from the same signer returns `alreadyExists: true`.
567
+ * - Distinct payload hashes from the same (slot, signer) are tracked in the
568
+ * equivocation index. The first one's bytes are stored; later distinct hashes
569
+ * bump `count` so libp2p can fire its duplicate callback.
450
570
  *
451
571
  * @param attestation - The checkpoint attestation to add
452
- * @param committeeSize - Committee size for the attestation's slot
453
- * @returns Result indicating whether the attestation was added and existence info
572
+ * @returns Result indicating whether the attestation was added, existence info,
573
+ * and number of distinct payload hashes by this signer for this slot
574
+ * (for equivocation detection).
454
575
  */
455
- public async tryAddCheckpointAttestation(
456
- attestation: CheckpointAttestation,
457
- committeeSize: number,
458
- ): Promise<TryAddResult> {
576
+ public async tryAddCheckpointAttestation(attestation: CheckpointAttestation): Promise<TryAddResult> {
459
577
  const slotNumber = attestation.payload.header.slotNumber;
460
- const proposalId = attestation.archive.toString();
461
578
  const sender = attestation.getSender();
462
579
 
463
580
  if (!sender) {
464
- return { added: false, alreadyExists: false, totalForPosition: 0 };
581
+ return { added: false, alreadyExists: false, count: 0 };
465
582
  }
466
583
 
467
- return await this.store.transactionAsync(async () => {
468
- const key = this.getAttestationKey(slotNumber, proposalId, sender.toString());
469
- const alreadyExists = await this.checkpointAttestations.hasAsync(key);
584
+ const signerAddress = sender.toString();
585
+ const slotSignerKey = this.getSlotSignerKey(slotNumber, signerAddress);
586
+ const payloadHash = attestation.getPayloadHash();
470
587
 
471
- if (alreadyExists) {
472
- const total = await this.getAttestationCount(slotNumber, proposalId);
473
- return { added: false, alreadyExists: true, totalForPosition: total };
588
+ return await this.store.transactionAsync(async () => {
589
+ if (await this.multimapHasValue(this.attestationHashesPerSlotAndSigner, slotSignerKey, payloadHash)) {
590
+ const count = await this.attestationHashesPerSlotAndSigner.getValueCountAsync(slotSignerKey);
591
+ return { added: false, alreadyExists: true, count };
474
592
  }
475
593
 
476
- const limit = committeeSize + ATTESTATION_CAP_BUFFER;
477
- const currentCount = await this.getAttestationCount(slotNumber, proposalId);
594
+ const signerAttestationCount = await this.attestationHashesPerSlotAndSigner.getValueCountAsync(slotSignerKey);
478
595
 
479
- if (currentCount >= limit) {
480
- return { added: false, alreadyExists: false, totalForPosition: currentCount };
596
+ if (signerAttestationCount >= MAX_ATTESTATIONS_PER_SLOT_AND_SIGNER) {
597
+ this.log.debug(`Rejecting attestation: signer ${signerAddress} exceeded per-slot cap for slot ${slotNumber}`, {
598
+ slotNumber,
599
+ signerAddress,
600
+ archive: attestation.archive.toString(),
601
+ payloadHash,
602
+ signerAttestationCount,
603
+ });
604
+ return {
605
+ added: false,
606
+ alreadyExists: false,
607
+ count: signerAttestationCount,
608
+ };
481
609
  }
482
610
 
483
- await this.checkpointAttestations.set(key, attestation.toBuffer());
611
+ // Track the new payload hash for equivocation detection.
612
+ await this.attestationHashesPerSlotAndSigner.set(slotSignerKey, payloadHash);
613
+
614
+ // Only the first distinct payload at (slot, signer) is stored; later
615
+ // equivocations are detected via the multimap but their bytes are not retained.
616
+ const alreadyHasStored = await this.attestationPerSlotAndSigner.hasAsync(slotSignerKey);
617
+ if (!alreadyHasStored) {
618
+ // Store the signature in canonical (v ∈ {27, 28}) form. `sender` recovered above, so the
619
+ // signature is non-empty and safe to normalize; this keeps a yParity-encoded (v = 0/1) variant
620
+ // from a malicious peer or an in-flight byte mutation out of the L1 bundle downstream.
621
+ await this.attestationPerSlotAndSigner.set(slotSignerKey, attestation.withNormalizedSignature().toBuffer());
622
+ this.metrics.trackMempoolItemAdded(slotSignerKey);
623
+ }
484
624
 
485
- this.log.debug(`Added checkpoint attestation for slot ${slotNumber} from ${sender.toString()}`, {
625
+ this.log.debug(`Added checkpoint attestation for slot ${slotNumber} from ${signerAddress}`, {
486
626
  signature: attestation.signature.toString(),
487
627
  slotNumber,
488
- address: sender.toString(),
489
- proposalId,
628
+ address: signerAddress,
629
+ archive: attestation.archive.toString(),
630
+ payloadHash,
631
+ stored: !alreadyHasStored,
490
632
  });
491
- return { added: true, alreadyExists: false, totalForPosition: currentCount + 1 };
492
- });
493
- }
494
633
 
495
- /** Gets the count of attestations for a given (slot, proposalId). */
496
- private async getAttestationCount(slot: SlotNumber, proposalId: string): Promise<number> {
497
- const range = this.getAttestationKeyRangeForProposal(slot, proposalId);
498
- let count = 0;
499
- for await (const _ of this.checkpointAttestations.keysAsync(range)) {
500
- count++;
501
- }
502
- return count;
634
+ return {
635
+ added: true,
636
+ alreadyExists: false,
637
+ count: signerAttestationCount + 1,
638
+ };
639
+ });
503
640
  }
504
641
  }
505
642