@aztec/p2p 0.0.1-commit.e558bd1c → 0.0.1-commit.e57c76e

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