@aztec/p2p 0.0.1-commit.c31f2472 → 0.0.1-commit.c52d6e7

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 (559) 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 +13 -12
  6. package/dest/client/factory.d.ts.map +1 -1
  7. package/dest/client/factory.js +67 -18
  8. package/dest/client/interface.d.ts +93 -40
  9. package/dest/client/interface.d.ts.map +1 -1
  10. package/dest/client/p2p_client.d.ts +69 -64
  11. package/dest/client/p2p_client.d.ts.map +1 -1
  12. package/dest/client/p2p_client.js +247 -244
  13. package/dest/config.d.ts +168 -89
  14. package/dest/config.d.ts.map +1 -1
  15. package/dest/config.js +157 -45
  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 +164 -106
  29. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  30. package/dest/mem_pools/attestation_pool/attestation_pool.js +518 -3
  31. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +2 -2
  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 +550 -125
  34. package/dest/mem_pools/attestation_pool/index.d.ts +2 -3
  35. package/dest/mem_pools/attestation_pool/index.d.ts.map +1 -1
  36. package/dest/mem_pools/attestation_pool/index.js +1 -2
  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 +3 -3
  41. package/dest/mem_pools/index.d.ts.map +1 -1
  42. package/dest/mem_pools/index.js +1 -1
  43. package/dest/mem_pools/instrumentation.d.ts +12 -5
  44. package/dest/mem_pools/instrumentation.d.ts.map +1 -1
  45. package/dest/mem_pools/instrumentation.js +38 -17
  46. package/dest/mem_pools/interface.d.ts +5 -5
  47. package/dest/mem_pools/interface.d.ts.map +1 -1
  48. package/dest/mem_pools/tx_pool_v2/archive/index.d.ts +2 -0
  49. package/dest/mem_pools/tx_pool_v2/archive/index.d.ts.map +1 -0
  50. package/dest/mem_pools/tx_pool_v2/archive/index.js +1 -0
  51. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts +39 -0
  52. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts.map +1 -0
  53. package/dest/mem_pools/tx_pool_v2/archive/tx_archive.js +97 -0
  54. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +104 -0
  55. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -0
  56. package/dest/mem_pools/tx_pool_v2/deleted_pool.js +251 -0
  57. package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts +47 -0
  58. package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts.map +1 -0
  59. package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.js +128 -0
  60. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts +17 -0
  61. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -0
  62. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +94 -0
  63. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts +19 -0
  64. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts.map +1 -0
  65. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.js +99 -0
  66. package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts +11 -0
  67. package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts.map +1 -0
  68. package/dest/mem_pools/tx_pool_v2/eviction/index.js +12 -0
  69. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts +16 -0
  70. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts.map +1 -0
  71. package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.js +64 -0
  72. package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts +180 -0
  73. package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts.map +1 -0
  74. package/dest/mem_pools/tx_pool_v2/eviction/interfaces.js +25 -0
  75. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts +15 -0
  76. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts.map +1 -0
  77. package/dest/mem_pools/{tx_pool → tx_pool_v2}/eviction/invalid_txs_after_mining_rule.js +15 -35
  78. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts +17 -0
  79. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts.map +1 -0
  80. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +92 -0
  81. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts +16 -0
  82. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts.map +1 -0
  83. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.js +79 -0
  84. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts +20 -0
  85. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts.map +1 -0
  86. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.js +75 -0
  87. package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts +15 -0
  88. package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts.map +1 -0
  89. package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.js +19 -0
  90. package/dest/mem_pools/tx_pool_v2/index.d.ts +6 -0
  91. package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -0
  92. package/dest/mem_pools/tx_pool_v2/index.js +5 -0
  93. package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts +15 -0
  94. package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts.map +1 -0
  95. package/dest/mem_pools/tx_pool_v2/instrumentation.js +43 -0
  96. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +243 -0
  97. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -0
  98. package/dest/mem_pools/tx_pool_v2/interfaces.js +11 -0
  99. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +137 -0
  100. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -0
  101. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +223 -0
  102. package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.d.ts +26 -0
  103. package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.d.ts.map +1 -0
  104. package/dest/mem_pools/tx_pool_v2/tx_pool_bench_metrics.js +70 -0
  105. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +122 -0
  106. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -0
  107. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +368 -0
  108. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +68 -0
  109. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -0
  110. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +178 -0
  111. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +91 -0
  112. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -0
  113. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +1013 -0
  114. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +10 -3
  115. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
  116. package/dest/msg_validators/attestation_validator/attestation_validator.js +38 -15
  117. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +8 -4
  118. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
  119. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +4 -5
  120. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +12 -5
  121. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
  122. package/dest/msg_validators/proposal_validator/block_proposal_validator.js +11 -3
  123. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +12 -5
  124. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
  125. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +23 -3
  126. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +36 -9
  127. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
  128. package/dest/msg_validators/proposal_validator/proposal_validator.js +130 -48
  129. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +4 -4
  130. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
  131. package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +11 -18
  132. package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts +2 -1
  133. package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts.map +1 -1
  134. package/dest/msg_validators/tx_validator/allowed_public_setup.js +25 -21
  135. package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts +17 -0
  136. package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts.map +1 -0
  137. package/dest/msg_validators/tx_validator/allowed_setup_helpers.js +24 -0
  138. package/dest/msg_validators/tx_validator/archive_cache.d.ts +3 -3
  139. package/dest/msg_validators/tx_validator/archive_cache.d.ts.map +1 -1
  140. package/dest/msg_validators/tx_validator/block_header_validator.d.ts +18 -5
  141. package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
  142. package/dest/msg_validators/tx_validator/block_header_validator.js +2 -2
  143. package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts +15 -0
  144. package/dest/msg_validators/tx_validator/cached_tx_validator.d.ts.map +1 -0
  145. package/dest/msg_validators/tx_validator/cached_tx_validator.js +19 -0
  146. package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts +9 -0
  147. package/dest/msg_validators/tx_validator/contract_instance_validator.d.ts.map +1 -0
  148. package/dest/msg_validators/tx_validator/contract_instance_validator.js +48 -0
  149. package/dest/msg_validators/tx_validator/data_validator.d.ts +2 -1
  150. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
  151. package/dest/msg_validators/tx_validator/data_validator.js +36 -2
  152. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +13 -3
  153. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
  154. package/dest/msg_validators/tx_validator/double_spend_validator.js +4 -4
  155. package/dest/msg_validators/tx_validator/factory.d.ts +133 -7
  156. package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
  157. package/dest/msg_validators/tx_validator/factory.js +261 -61
  158. package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts +1 -1
  159. package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts.map +1 -1
  160. package/dest/msg_validators/tx_validator/fee_payer_balance.js +6 -2
  161. package/dest/msg_validators/tx_validator/gas_validator.d.ts +104 -3
  162. package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
  163. package/dest/msg_validators/tx_validator/gas_validator.js +137 -53
  164. package/dest/msg_validators/tx_validator/index.d.ts +5 -1
  165. package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
  166. package/dest/msg_validators/tx_validator/index.js +4 -0
  167. package/dest/msg_validators/tx_validator/metadata_validator.d.ts +1 -1
  168. package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
  169. package/dest/msg_validators/tx_validator/metadata_validator.js +4 -4
  170. package/dest/msg_validators/tx_validator/nullifier_cache.d.ts +14 -0
  171. package/dest/msg_validators/tx_validator/nullifier_cache.d.ts.map +1 -0
  172. package/dest/msg_validators/tx_validator/nullifier_cache.js +24 -0
  173. package/dest/msg_validators/tx_validator/phases_validator.d.ts +22 -2
  174. package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
  175. package/dest/msg_validators/tx_validator/phases_validator.js +72 -24
  176. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +20 -4
  177. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -1
  178. package/dest/msg_validators/tx_validator/timestamp_validator.js +7 -7
  179. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts +2 -1
  180. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -1
  181. package/dest/msg_validators/tx_validator/tx_proof_validator.js +2 -0
  182. package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts +48 -0
  183. package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts.map +1 -0
  184. package/dest/msg_validators/tx_validator/tx_validation_cache.js +68 -0
  185. package/dest/services/data_store.d.ts +1 -1
  186. package/dest/services/data_store.d.ts.map +1 -1
  187. package/dest/services/data_store.js +14 -10
  188. package/dest/services/discv5/discV5_service.d.ts +13 -2
  189. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  190. package/dest/services/discv5/discV5_service.js +119 -23
  191. package/dest/services/discv5/persisted_enr_store.d.ts +38 -0
  192. package/dest/services/discv5/persisted_enr_store.d.ts.map +1 -0
  193. package/dest/services/discv5/persisted_enr_store.js +145 -0
  194. package/dest/services/dummy_service.d.ts +26 -18
  195. package/dest/services/dummy_service.d.ts.map +1 -1
  196. package/dest/services/dummy_service.js +25 -20
  197. package/dest/services/encoding.d.ts +24 -7
  198. package/dest/services/encoding.d.ts.map +1 -1
  199. package/dest/services/encoding.js +43 -26
  200. package/dest/services/gossipsub/index.d.ts +3 -0
  201. package/dest/services/gossipsub/index.d.ts.map +1 -0
  202. package/dest/services/gossipsub/index.js +2 -0
  203. package/dest/services/gossipsub/scoring.d.ts +21 -3
  204. package/dest/services/gossipsub/scoring.d.ts.map +1 -1
  205. package/dest/services/gossipsub/scoring.js +24 -7
  206. package/dest/services/gossipsub/topic_score_params.d.ts +167 -0
  207. package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -0
  208. package/dest/services/gossipsub/topic_score_params.js +336 -0
  209. package/dest/services/index.d.ts +2 -1
  210. package/dest/services/index.d.ts.map +1 -1
  211. package/dest/services/index.js +1 -0
  212. package/dest/services/libp2p/instrumentation.d.ts +3 -1
  213. package/dest/services/libp2p/instrumentation.d.ts.map +1 -1
  214. package/dest/services/libp2p/instrumentation.js +14 -0
  215. package/dest/services/libp2p/libp2p_service.d.ts +112 -61
  216. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  217. package/dest/services/libp2p/libp2p_service.js +808 -583
  218. package/dest/services/peer-manager/metrics.d.ts +3 -1
  219. package/dest/services/peer-manager/metrics.d.ts.map +1 -1
  220. package/dest/services/peer-manager/metrics.js +6 -0
  221. package/dest/services/peer-manager/peer_manager.d.ts +20 -3
  222. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  223. package/dest/services/peer-manager/peer_manager.js +83 -26
  224. package/dest/services/peer-manager/peer_scoring.d.ts +34 -2
  225. package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
  226. package/dest/services/peer-manager/peer_scoring.js +127 -15
  227. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +7 -10
  228. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
  229. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +100 -132
  230. package/dest/services/reqresp/batch-tx-requester/interface.d.ts +11 -11
  231. package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -1
  232. package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts +11 -13
  233. package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts.map +1 -1
  234. package/dest/services/reqresp/batch-tx-requester/missing_txs.js +31 -46
  235. package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts +19 -11
  236. package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts.map +1 -1
  237. package/dest/services/reqresp/batch-tx-requester/peer_collection.js +52 -15
  238. package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts +5 -14
  239. package/dest/services/reqresp/batch-tx-requester/tx_validator.d.ts.map +1 -1
  240. package/dest/services/reqresp/batch-tx-requester/tx_validator.js +6 -20
  241. package/dest/services/reqresp/config.d.ts +15 -3
  242. package/dest/services/reqresp/config.d.ts.map +1 -1
  243. package/dest/services/reqresp/config.js +11 -0
  244. package/dest/services/reqresp/interface.d.ts +25 -18
  245. package/dest/services/reqresp/interface.d.ts.map +1 -1
  246. package/dest/services/reqresp/interface.js +23 -19
  247. package/dest/services/reqresp/metrics.d.ts +1 -1
  248. package/dest/services/reqresp/metrics.d.ts.map +1 -1
  249. package/dest/services/reqresp/metrics.js +0 -1
  250. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +4 -3
  251. package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -1
  252. package/dest/services/reqresp/protocols/block_txs/bitvector.js +3 -2
  253. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +7 -5
  254. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
  255. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +34 -26
  256. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +33 -17
  257. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
  258. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +72 -38
  259. package/dest/services/reqresp/protocols/index.d.ts +1 -2
  260. package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
  261. package/dest/services/reqresp/protocols/index.js +0 -1
  262. package/dest/services/reqresp/protocols/tx.d.ts +7 -1
  263. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
  264. package/dest/services/reqresp/protocols/tx.js +21 -3
  265. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +5 -4
  266. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
  267. package/dest/services/reqresp/rate-limiter/rate_limiter.js +10 -8
  268. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
  269. package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
  270. package/dest/services/reqresp/rate-limiter/rate_limits.js +3 -13
  271. package/dest/services/reqresp/reqresp.d.ts +7 -29
  272. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  273. package/dest/services/reqresp/reqresp.js +85 -219
  274. package/dest/services/service.d.ts +66 -13
  275. package/dest/services/service.d.ts.map +1 -1
  276. package/dest/services/tx_collection/config.d.ts +11 -14
  277. package/dest/services/tx_collection/config.d.ts.map +1 -1
  278. package/dest/services/tx_collection/config.js +26 -33
  279. package/dest/services/tx_collection/file_store_tx_collection.d.ts +37 -0
  280. package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -0
  281. package/dest/services/tx_collection/file_store_tx_collection.js +127 -0
  282. package/dest/services/tx_collection/file_store_tx_source.d.ts +38 -0
  283. package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -0
  284. package/dest/services/tx_collection/file_store_tx_source.js +100 -0
  285. package/dest/services/tx_collection/index.d.ts +3 -3
  286. package/dest/services/tx_collection/index.d.ts.map +1 -1
  287. package/dest/services/tx_collection/index.js +1 -1
  288. package/dest/services/tx_collection/instrumentation.js +1 -2
  289. package/dest/services/tx_collection/request_tracker.d.ts +53 -0
  290. package/dest/services/tx_collection/request_tracker.d.ts.map +1 -0
  291. package/dest/services/tx_collection/request_tracker.js +84 -0
  292. package/dest/services/tx_collection/tx_collection.d.ts +45 -51
  293. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
  294. package/dest/services/tx_collection/tx_collection.js +296 -68
  295. package/dest/services/tx_collection/tx_collection_sink.d.ts +19 -9
  296. package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
  297. package/dest/services/tx_collection/tx_collection_sink.js +28 -31
  298. package/dest/services/tx_collection/tx_source.d.ts +13 -7
  299. package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
  300. package/dest/services/tx_collection/tx_source.js +29 -7
  301. package/dest/services/tx_file_store/config.d.ts +16 -0
  302. package/dest/services/tx_file_store/config.d.ts.map +1 -0
  303. package/dest/services/tx_file_store/config.js +22 -0
  304. package/dest/services/tx_file_store/index.d.ts +4 -0
  305. package/dest/services/tx_file_store/index.d.ts.map +1 -0
  306. package/dest/services/tx_file_store/index.js +3 -0
  307. package/dest/services/tx_file_store/instrumentation.d.ts +15 -0
  308. package/dest/services/tx_file_store/instrumentation.d.ts.map +1 -0
  309. package/dest/services/tx_file_store/instrumentation.js +29 -0
  310. package/dest/services/tx_file_store/tx_file_store.d.ts +46 -0
  311. package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -0
  312. package/dest/services/tx_file_store/tx_file_store.js +142 -0
  313. package/dest/services/tx_provider.d.ts +6 -4
  314. package/dest/services/tx_provider.d.ts.map +1 -1
  315. package/dest/services/tx_provider.js +12 -8
  316. package/dest/test-helpers/make-test-p2p-clients.d.ts +7 -8
  317. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  318. package/dest/test-helpers/make-test-p2p-clients.js +5 -3
  319. package/dest/test-helpers/mock-pubsub.d.ts +46 -6
  320. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  321. package/dest/test-helpers/mock-pubsub.js +115 -14
  322. package/dest/test-helpers/reqresp-nodes.d.ts +5 -7
  323. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  324. package/dest/test-helpers/reqresp-nodes.js +19 -20
  325. package/dest/test-helpers/test_tx_provider.d.ts +3 -1
  326. package/dest/test-helpers/test_tx_provider.d.ts.map +1 -1
  327. package/dest/test-helpers/test_tx_provider.js +3 -0
  328. package/dest/test-helpers/testbench-utils.d.ts +58 -50
  329. package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
  330. package/dest/test-helpers/testbench-utils.js +181 -76
  331. package/dest/testbench/p2p_client_testbench_worker.d.ts +3 -5
  332. package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -1
  333. package/dest/testbench/p2p_client_testbench_worker.js +114 -51
  334. package/dest/testbench/worker_client_manager.d.ts +12 -6
  335. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  336. package/dest/testbench/worker_client_manager.js +57 -11
  337. package/dest/util.d.ts +13 -8
  338. package/dest/util.d.ts.map +1 -1
  339. package/dest/util.js +35 -14
  340. package/dest/versioning.d.ts +3 -6
  341. package/dest/versioning.d.ts.map +1 -1
  342. package/dest/versioning.js +3 -24
  343. package/package.json +15 -16
  344. package/src/bootstrap/bootstrap.ts +17 -3
  345. package/src/client/factory.ts +135 -31
  346. package/src/client/interface.ts +100 -48
  347. package/src/client/p2p_client.ts +289 -310
  348. package/src/client/test/{tx_proposal_collector/README.md → p2p_client.batch_tx_requester.bench.README.md} +23 -53
  349. package/src/config.ts +282 -49
  350. package/src/errors/p2p-service.error.ts +11 -0
  351. package/src/errors/reqresp.error.ts +13 -19
  352. package/src/errors/tx-pool.error.ts +12 -0
  353. package/src/index.ts +1 -1
  354. package/src/mem_pools/attestation_pool/attestation_pool.ts +585 -94
  355. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +656 -141
  356. package/src/mem_pools/attestation_pool/index.ts +9 -2
  357. package/src/mem_pools/attestation_pool/mocks.ts +14 -8
  358. package/src/mem_pools/index.ts +2 -2
  359. package/src/mem_pools/instrumentation.ts +27 -17
  360. package/src/mem_pools/interface.ts +4 -4
  361. package/src/mem_pools/tx_pool_v2/README.md +283 -0
  362. package/src/mem_pools/tx_pool_v2/archive/index.ts +1 -0
  363. package/src/mem_pools/tx_pool_v2/archive/tx_archive.ts +112 -0
  364. package/src/mem_pools/tx_pool_v2/deleted_pool.ts +321 -0
  365. package/src/mem_pools/tx_pool_v2/eviction/eviction_manager.ts +160 -0
  366. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +120 -0
  367. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +126 -0
  368. package/src/mem_pools/tx_pool_v2/eviction/index.ts +28 -0
  369. package/src/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.ts +67 -0
  370. package/src/mem_pools/tx_pool_v2/eviction/interfaces.ts +219 -0
  371. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.ts +72 -0
  372. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +99 -0
  373. package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +90 -0
  374. package/src/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.ts +99 -0
  375. package/src/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.ts +32 -0
  376. package/src/mem_pools/tx_pool_v2/index.ts +12 -0
  377. package/src/mem_pools/tx_pool_v2/instrumentation.ts +69 -0
  378. package/src/mem_pools/tx_pool_v2/interfaces.ts +274 -0
  379. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +349 -0
  380. package/src/mem_pools/tx_pool_v2/tx_pool_bench_metrics.ts +77 -0
  381. package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +465 -0
  382. package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +252 -0
  383. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +1215 -0
  384. package/src/msg_validators/attestation_validator/README.md +49 -0
  385. package/src/msg_validators/attestation_validator/attestation_validator.ts +46 -14
  386. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +17 -9
  387. package/src/msg_validators/proposal_validator/README.md +123 -0
  388. package/src/msg_validators/proposal_validator/block_proposal_validator.ts +26 -4
  389. package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +43 -7
  390. package/src/msg_validators/proposal_validator/proposal_validator.ts +155 -49
  391. package/src/msg_validators/tx_validator/README.md +129 -0
  392. package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +8 -17
  393. package/src/msg_validators/tx_validator/allowed_public_setup.ts +22 -27
  394. package/src/msg_validators/tx_validator/allowed_setup_helpers.ts +31 -0
  395. package/src/msg_validators/tx_validator/archive_cache.ts +2 -2
  396. package/src/msg_validators/tx_validator/block_header_validator.ts +18 -8
  397. package/src/msg_validators/tx_validator/cached_tx_validator.ts +31 -0
  398. package/src/msg_validators/tx_validator/contract_instance_validator.ts +56 -0
  399. package/src/msg_validators/tx_validator/data_validator.ts +44 -1
  400. package/src/msg_validators/tx_validator/double_spend_validator.ts +11 -6
  401. package/src/msg_validators/tx_validator/factory.ts +413 -80
  402. package/src/msg_validators/tx_validator/fee_payer_balance.ts +6 -2
  403. package/src/msg_validators/tx_validator/gas_validator.ts +195 -54
  404. package/src/msg_validators/tx_validator/index.ts +4 -0
  405. package/src/msg_validators/tx_validator/metadata_validator.ts +12 -4
  406. package/src/msg_validators/tx_validator/nullifier_cache.ts +30 -0
  407. package/src/msg_validators/tx_validator/phases_validator.ts +82 -27
  408. package/src/msg_validators/tx_validator/timestamp_validator.ts +24 -19
  409. package/src/msg_validators/tx_validator/tx_proof_validator.ts +2 -0
  410. package/src/msg_validators/tx_validator/tx_validation_cache.ts +101 -0
  411. package/src/services/data_store.ts +14 -19
  412. package/src/services/discv5/discV5_service.ts +126 -14
  413. package/src/services/discv5/persisted_enr_store.ts +158 -0
  414. package/src/services/dummy_service.ts +38 -45
  415. package/src/services/encoding.ts +42 -26
  416. package/src/services/gossipsub/README.md +663 -0
  417. package/src/services/gossipsub/index.ts +2 -0
  418. package/src/services/gossipsub/scoring.ts +29 -5
  419. package/src/services/gossipsub/topic_score_params.ts +478 -0
  420. package/src/services/index.ts +1 -0
  421. package/src/services/libp2p/instrumentation.ts +14 -0
  422. package/src/services/libp2p/libp2p_service.ts +880 -641
  423. package/src/services/peer-manager/metrics.ts +7 -0
  424. package/src/services/peer-manager/peer_manager.ts +91 -25
  425. package/src/services/peer-manager/peer_scoring.ts +127 -8
  426. package/src/services/reqresp/README.md +194 -0
  427. package/src/services/reqresp/batch-tx-requester/README.md +60 -21
  428. package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +101 -147
  429. package/src/services/reqresp/batch-tx-requester/interface.ts +14 -10
  430. package/src/services/reqresp/batch-tx-requester/missing_txs.ts +30 -71
  431. package/src/services/reqresp/batch-tx-requester/peer_collection.ts +68 -24
  432. package/src/services/reqresp/batch-tx-requester/tx_validator.ts +12 -25
  433. package/src/services/reqresp/config.ts +19 -2
  434. package/src/services/reqresp/interface.ts +45 -46
  435. package/src/services/reqresp/metrics.ts +0 -1
  436. package/src/services/reqresp/protocols/block_txs/bitvector.ts +3 -2
  437. package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +43 -30
  438. package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +86 -36
  439. package/src/services/reqresp/protocols/index.ts +0 -1
  440. package/src/services/reqresp/protocols/tx.ts +23 -3
  441. package/src/services/reqresp/rate-limiter/rate_limiter.ts +13 -9
  442. package/src/services/reqresp/rate-limiter/rate_limits.ts +3 -13
  443. package/src/services/reqresp/reqresp.ts +100 -263
  444. package/src/services/service.ts +85 -29
  445. package/src/services/tx_collection/config.ts +40 -49
  446. package/src/services/tx_collection/file_store_tx_collection.ts +153 -0
  447. package/src/services/tx_collection/file_store_tx_source.ts +129 -0
  448. package/src/services/tx_collection/index.ts +2 -6
  449. package/src/services/tx_collection/instrumentation.ts +1 -1
  450. package/src/services/tx_collection/request_tracker.ts +127 -0
  451. package/src/services/tx_collection/tx_collection.ts +362 -110
  452. package/src/services/tx_collection/tx_collection_sink.ts +32 -36
  453. package/src/services/tx_collection/tx_source.ts +29 -8
  454. package/src/services/tx_file_store/config.ts +37 -0
  455. package/src/services/tx_file_store/index.ts +3 -0
  456. package/src/services/tx_file_store/instrumentation.ts +36 -0
  457. package/src/services/tx_file_store/tx_file_store.ts +163 -0
  458. package/src/services/tx_provider.ts +15 -9
  459. package/src/test-helpers/make-test-p2p-clients.ts +7 -6
  460. package/src/test-helpers/mock-pubsub.ts +137 -14
  461. package/src/test-helpers/reqresp-nodes.ts +17 -29
  462. package/src/test-helpers/test_tx_provider.ts +5 -0
  463. package/src/test-helpers/testbench-utils.ts +188 -94
  464. package/src/testbench/p2p_client_testbench_worker.ts +123 -66
  465. package/src/testbench/worker_client_manager.ts +72 -25
  466. package/src/util.ts +40 -19
  467. package/src/versioning.ts +3 -33
  468. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts +0 -2
  469. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.d.ts.map +0 -1
  470. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +0 -305
  471. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts +0 -73
  472. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.d.ts.map +0 -1
  473. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.js +0 -8
  474. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +0 -40
  475. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +0 -1
  476. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +0 -218
  477. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +0 -31
  478. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +0 -1
  479. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +0 -180
  480. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +0 -125
  481. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +0 -1
  482. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +0 -596
  483. package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts +0 -32
  484. package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts.map +0 -1
  485. package/dest/mem_pools/tx_pool/eviction/eviction_manager.js +0 -112
  486. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts +0 -157
  487. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts.map +0 -1
  488. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.js +0 -52
  489. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts +0 -16
  490. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts.map +0 -1
  491. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.js +0 -122
  492. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts +0 -17
  493. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts.map +0 -1
  494. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts +0 -19
  495. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts.map +0 -1
  496. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.js +0 -78
  497. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts +0 -26
  498. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts.map +0 -1
  499. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.js +0 -84
  500. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts +0 -25
  501. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts.map +0 -1
  502. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.js +0 -57
  503. package/dest/mem_pools/tx_pool/index.d.ts +0 -3
  504. package/dest/mem_pools/tx_pool/index.d.ts.map +0 -1
  505. package/dest/mem_pools/tx_pool/index.js +0 -2
  506. package/dest/mem_pools/tx_pool/priority.d.ts +0 -12
  507. package/dest/mem_pools/tx_pool/priority.d.ts.map +0 -1
  508. package/dest/mem_pools/tx_pool/priority.js +0 -15
  509. package/dest/mem_pools/tx_pool/tx_pool.d.ts +0 -127
  510. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +0 -1
  511. package/dest/mem_pools/tx_pool/tx_pool.js +0 -3
  512. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +0 -7
  513. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +0 -1
  514. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +0 -400
  515. package/dest/msg_validators/clock_tolerance.d.ts +0 -21
  516. package/dest/msg_validators/clock_tolerance.d.ts.map +0 -1
  517. package/dest/msg_validators/clock_tolerance.js +0 -37
  518. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts +0 -23
  519. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts.map +0 -1
  520. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.js +0 -212
  521. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts +0 -64
  522. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +0 -1
  523. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.js +0 -151
  524. package/dest/services/reqresp/protocols/block.d.ts +0 -9
  525. package/dest/services/reqresp/protocols/block.d.ts.map +0 -1
  526. package/dest/services/reqresp/protocols/block.js +0 -32
  527. package/dest/services/tx_collection/fast_tx_collection.d.ts +0 -53
  528. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +0 -1
  529. package/dest/services/tx_collection/fast_tx_collection.js +0 -311
  530. package/dest/services/tx_collection/proposal_tx_collector.d.ts +0 -48
  531. package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +0 -1
  532. package/dest/services/tx_collection/proposal_tx_collector.js +0 -50
  533. package/dest/services/tx_collection/slow_tx_collection.d.ts +0 -53
  534. package/dest/services/tx_collection/slow_tx_collection.d.ts.map +0 -1
  535. package/dest/services/tx_collection/slow_tx_collection.js +0 -177
  536. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +0 -336
  537. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker_protocol.ts +0 -43
  538. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +0 -320
  539. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +0 -264
  540. package/src/mem_pools/tx_pool/README.md +0 -270
  541. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +0 -746
  542. package/src/mem_pools/tx_pool/eviction/eviction_manager.ts +0 -132
  543. package/src/mem_pools/tx_pool/eviction/eviction_strategy.ts +0 -208
  544. package/src/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.ts +0 -162
  545. package/src/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.ts +0 -104
  546. package/src/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.ts +0 -93
  547. package/src/mem_pools/tx_pool/eviction/low_priority_eviction_rule.ts +0 -106
  548. package/src/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.ts +0 -75
  549. package/src/mem_pools/tx_pool/index.ts +0 -2
  550. package/src/mem_pools/tx_pool/priority.ts +0 -20
  551. package/src/mem_pools/tx_pool/tx_pool.ts +0 -141
  552. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +0 -319
  553. package/src/msg_validators/clock_tolerance.ts +0 -51
  554. package/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts +0 -230
  555. package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +0 -161
  556. package/src/services/reqresp/protocols/block.ts +0 -37
  557. package/src/services/tx_collection/fast_tx_collection.ts +0 -364
  558. package/src/services/tx_collection/proposal_tx_collector.ts +0 -114
  559. package/src/services/tx_collection/slow_tx_collection.ts +0 -233
@@ -1,7 +1,9 @@
1
- import { SlotNumber } from '@aztec/foundation/branded-types';
1
+ import { IndexWithinCheckpoint, SlotNumber } from '@aztec/foundation/branded-types';
2
2
  import { Secp256k1Signer } from '@aztec/foundation/crypto/secp256k1-signer';
3
3
  import { Fr } from '@aztec/foundation/curves/bn254';
4
- import type { BlockProposal, CheckpointAttestation, CheckpointProposal } from '@aztec/stdlib/p2p';
4
+ import { Signature } from '@aztec/foundation/eth-signature';
5
+ import type { BlockProposal, CheckpointProposalCore } from '@aztec/stdlib/p2p';
6
+ import { CheckpointAttestation } from '@aztec/stdlib/p2p';
5
7
  import { CheckpointHeader } from '@aztec/stdlib/rollup';
6
8
  import {
7
9
  makeBlockHeader,
@@ -10,8 +12,11 @@ import {
10
12
  makeCheckpointProposal,
11
13
  } from '@aztec/stdlib/testing';
12
14
 
13
- import type { AttestationPool } from './attestation_pool.js';
14
- import { MAX_PROPOSALS_PER_SLOT } from './kv_attestation_pool.js';
15
+ import {
16
+ type AttestationPool,
17
+ MAX_BLOCK_PROPOSALS_PER_POSITION,
18
+ MAX_CHECKPOINT_PROPOSALS_PER_SLOT,
19
+ } from './attestation_pool.js';
15
20
  import { mockCheckpointAttestation } from './mocks.js';
16
21
 
17
22
  const NUMBER_OF_SIGNERS_PER_TEST = 4;
@@ -25,9 +30,16 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
25
30
  signers = Array.from({ length: NUMBER_OF_SIGNERS_PER_TEST }, () => Secp256k1Signer.random());
26
31
  });
27
32
 
33
+ /**
34
+ * Build attestations from each signer over the *same* signed payload (same header,
35
+ * archive, feeAssetPriceModifier). Required by the new pool, which deduplicates by
36
+ * payload hash and treats attestations from different signers as distinct entries
37
+ * only when the payload itself matches.
38
+ */
28
39
  const createCheckpointAttestationsForSlot = (slotNumber: number, archive?: Fr) => {
29
40
  const archiveToUse = archive ?? Fr.random();
30
- return signers.map(signer => mockCheckpointAttestation(signer, slotNumber, archiveToUse));
41
+ const sharedHeader = CheckpointHeader.random({ slotNumber: SlotNumber(slotNumber) });
42
+ return signers.map(signer => mockCheckpointAttestation(signer, slotNumber, archiveToUse, sharedHeader));
31
43
  };
32
44
 
33
45
  const mockBlockProposalForPool = (
@@ -52,57 +64,92 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
52
64
  expect(a1Buffer).toEqual(expect.arrayContaining(a2Buffer));
53
65
  };
54
66
 
67
+ /**
68
+ * Rewrites the attester signature into yParity form (27 -> 0, 28 -> 1), keeping (r, s). The recovery
69
+ * bit is unchanged so it still recovers to the same signer, mimicking a peer that gossips (or mutates
70
+ * in flight) a signature with a non-canonical recovery byte.
71
+ */
72
+ const toYParityForm = (attestation: CheckpointAttestation): CheckpointAttestation => {
73
+ const sig = attestation.signature;
74
+ const yParityV = sig.v === 27 ? 0 : sig.v === 28 ? 1 : sig.v;
75
+ return new CheckpointAttestation(
76
+ attestation.payload,
77
+ new Signature(sig.r, sig.s, yParityV),
78
+ attestation.proposerSignature,
79
+ );
80
+ };
81
+
55
82
  describe('CheckpointAttestation', () => {
83
+ it('normalizes yParity (v=0/v=1) attester signatures on ingress from both gossip and own keys', async () => {
84
+ const slotNumber = 421;
85
+ const canonical = createCheckpointAttestationsForSlot(slotNumber);
86
+ const payloadHash = canonical[0].getPayloadHash();
87
+ const expectedSenders = new Set([canonical[0].getSender()!.toString(), canonical[1].getSender()!.toString()]);
88
+
89
+ const gossiped = toYParityForm(canonical[0]);
90
+ expect([0, 1]).toContain(gossiped.signature.v);
91
+ await ap.tryAddCheckpointAttestation(gossiped);
92
+
93
+ const own = toYParityForm(canonical[1]);
94
+ expect([0, 1]).toContain(own.signature.v);
95
+ await ap.addOwnCheckpointAttestations([own]);
96
+
97
+ const retrieved = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(slotNumber), payloadHash);
98
+ expect(retrieved.length).toBe(2);
99
+ for (const attestation of retrieved) {
100
+ expect([27, 28]).toContain(attestation.signature.v);
101
+ expect(expectedSenders.has(attestation.getSender()!.toString())).toBe(true);
102
+ }
103
+ });
104
+
56
105
  it('should add attestations to pool', async () => {
57
106
  const slotNumber = 420;
58
107
  const archive = Fr.random();
59
- const attestations = signers.slice(0, -1).map(signer => mockCheckpointAttestation(signer, slotNumber, archive));
108
+ const sharedHeader = CheckpointHeader.random({ slotNumber: SlotNumber(slotNumber) });
109
+ const attestations = signers
110
+ .slice(0, -1)
111
+ .map(signer => mockCheckpointAttestation(signer, slotNumber, archive, sharedHeader));
112
+ const payloadHash = attestations[0].getPayloadHash();
60
113
 
61
- await ap.addCheckpointAttestations(attestations);
114
+ await ap.addOwnCheckpointAttestations(attestations);
62
115
 
63
116
  const retrievedAttestations = await ap.getCheckpointAttestationsForSlotAndProposal(
64
117
  SlotNumber(slotNumber),
65
- archive.toString(),
118
+ payloadHash,
66
119
  );
67
120
  expect(retrievedAttestations.length).toBe(attestations.length);
68
121
  compareCheckpointAttestations(retrievedAttestations, attestations);
69
122
 
70
- // Check hasCheckpointAttestation for added attestations
71
- for (const attestation of attestations) {
72
- expect(await ap.hasCheckpointAttestation(attestation)).toBe(true);
73
- }
74
-
75
123
  const retrievedAttestationsForSlot = await ap.getCheckpointAttestationsForSlot(SlotNumber(slotNumber));
76
124
  expect(retrievedAttestationsForSlot.length).toBe(attestations.length);
77
125
  compareCheckpointAttestations(retrievedAttestationsForSlot, attestations);
78
126
 
79
127
  // Add another one
80
- const newAttestation = mockCheckpointAttestation(signers[NUMBER_OF_SIGNERS_PER_TEST - 1], slotNumber, archive);
81
- await ap.addCheckpointAttestations([newAttestation]);
128
+ const newAttestation = mockCheckpointAttestation(
129
+ signers[NUMBER_OF_SIGNERS_PER_TEST - 1],
130
+ slotNumber,
131
+ archive,
132
+ sharedHeader,
133
+ );
134
+ await ap.addOwnCheckpointAttestations([newAttestation]);
82
135
  const retrievedAttestationsAfterAdd = await ap.getCheckpointAttestationsForSlotAndProposal(
83
136
  SlotNumber(slotNumber),
84
- archive.toString(),
137
+ payloadHash,
85
138
  );
86
139
  expect(retrievedAttestationsAfterAdd.length).toBe(attestations.length + 1);
87
140
  compareCheckpointAttestations(retrievedAttestationsAfterAdd, [...attestations, newAttestation]);
88
- expect(await ap.hasCheckpointAttestation(newAttestation)).toBe(true);
89
141
  const retrievedAttestationsForSlotAfterAdd = await ap.getCheckpointAttestationsForSlot(SlotNumber(slotNumber));
90
142
  expect(retrievedAttestationsForSlotAfterAdd.length).toBe(attestations.length + 1);
91
143
  compareCheckpointAttestations(retrievedAttestationsForSlotAfterAdd, [...attestations, newAttestation]);
92
144
 
93
145
  // Delete by slot
94
- await ap.deleteCheckpointAttestationsOlderThan(SlotNumber(slotNumber + 1));
146
+ await ap.deleteOlderThan(SlotNumber(slotNumber + 1));
95
147
 
96
148
  const retreivedAttestationsAfterDelete = await ap.getCheckpointAttestationsForSlotAndProposal(
97
149
  SlotNumber(slotNumber),
98
- archive.toString(),
150
+ payloadHash,
99
151
  );
100
152
  expect(retreivedAttestationsAfterDelete.length).toBe(0);
101
- // Check hasCheckpointAttestation after deletion
102
- for (const attestation of attestations) {
103
- expect(await ap.hasCheckpointAttestation(attestation)).toBe(false);
104
- }
105
- expect(await ap.hasCheckpointAttestation(newAttestation)).toBe(false);
106
153
  });
107
154
 
108
155
  it('should handle duplicate proposals in a slot', async () => {
@@ -116,36 +163,35 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
116
163
  for (let i = 0; i < NUMBER_OF_SIGNERS_PER_TEST; i++) {
117
164
  attestations.push(mockCheckpointAttestation(signer, slotNumber, archive, header));
118
165
  }
166
+ const payloadHash = attestations[0].getPayloadHash();
119
167
 
120
168
  // Add them to store and check we end up with only one
121
- await ap.addCheckpointAttestations(attestations);
169
+ await ap.addOwnCheckpointAttestations(attestations);
122
170
 
123
171
  const retreivedAttestations = await ap.getCheckpointAttestationsForSlotAndProposal(
124
172
  SlotNumber(slotNumber),
125
- archive.toString(),
173
+ payloadHash,
126
174
  );
127
175
  expect(retreivedAttestations.length).toBe(1);
128
176
  expect(retreivedAttestations[0].toBuffer()).toEqual(attestations[0].toBuffer());
129
177
  expect(retreivedAttestations[0].getSender()?.toString()).toEqual(signer.address.toString());
130
178
 
131
179
  // Try adding them on another operation and check they are still not duplicated
132
- await ap.addCheckpointAttestations([attestations[0]]);
133
- expect(
134
- await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(slotNumber), archive.toString()),
135
- ).toHaveLength(1);
180
+ await ap.addOwnCheckpointAttestations([attestations[0]]);
181
+ expect(await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(slotNumber), payloadHash)).toHaveLength(1);
136
182
  });
137
183
 
138
184
  it('should store attestations by differing slot', async () => {
139
185
  const slotNumbers = [1, 2, 3, 4];
140
186
  const attestations = signers.map((signer, i) => mockCheckpointAttestation(signer, slotNumbers[i]));
141
187
 
142
- await ap.addCheckpointAttestations(attestations);
188
+ await ap.addOwnCheckpointAttestations(attestations);
143
189
 
144
190
  for (const attestation of attestations) {
145
191
  const slot = attestation.payload.header.slotNumber;
146
- const archive = attestation.archive.toString();
192
+ const payloadHash = attestation.getPayloadHash();
147
193
 
148
- const retreivedAttestations = await ap.getCheckpointAttestationsForSlotAndProposal(slot, archive);
194
+ const retreivedAttestations = await ap.getCheckpointAttestationsForSlotAndProposal(slot, payloadHash);
149
195
  expect(retreivedAttestations.length).toBe(1);
150
196
  expect(retreivedAttestations[0].toBuffer()).toEqual(attestation.toBuffer());
151
197
  expect(retreivedAttestations[0].payload.header.slotNumber).toEqual(slot);
@@ -157,13 +203,13 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
157
203
  const archives = [Fr.random(), Fr.random(), Fr.random(), Fr.random()];
158
204
  const attestations = signers.map((signer, i) => mockCheckpointAttestation(signer, slotNumbers[i], archives[i]));
159
205
 
160
- await ap.addCheckpointAttestations(attestations);
206
+ await ap.addOwnCheckpointAttestations(attestations);
161
207
 
162
208
  for (const attestation of attestations) {
163
209
  const slot = attestation.payload.header.slotNumber;
164
- const proposalId = attestation.archive.toString();
210
+ const payloadHash = attestation.getPayloadHash();
165
211
 
166
- const retreivedAttestations = await ap.getCheckpointAttestationsForSlotAndProposal(slot, proposalId);
212
+ const retreivedAttestations = await ap.getCheckpointAttestationsForSlotAndProposal(slot, payloadHash);
167
213
  expect(retreivedAttestations.length).toBe(1);
168
214
  expect(retreivedAttestations[0].toBuffer()).toEqual(attestation.toBuffer());
169
215
  expect(retreivedAttestations[0].payload.header.slotNumber).toEqual(slot);
@@ -172,21 +218,25 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
172
218
 
173
219
  it('should delete attestations older than a given slot', async () => {
174
220
  const slotNumbers = [1, 2, 3, 69, 72, 74, 88, 420];
175
- const attestations = (
176
- await Promise.all(slotNumbers.map(slotNumber => createCheckpointAttestationsForSlot(slotNumber)))
177
- ).flat();
178
- const proposalId = attestations[0].archive.toString();
221
+ const attestationsPerSlot = await Promise.all(
222
+ slotNumbers.map(slotNumber => createCheckpointAttestationsForSlot(slotNumber)),
223
+ );
224
+ const attestations = attestationsPerSlot.flat();
225
+ const payloadHashForSlot1 = attestationsPerSlot[0][0].getPayloadHash();
179
226
 
180
- await ap.addCheckpointAttestations(attestations);
227
+ await ap.addOwnCheckpointAttestations(attestations);
181
228
 
182
- const attestationsForSlot1 = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(1), proposalId);
229
+ const attestationsForSlot1 = await ap.getCheckpointAttestationsForSlotAndProposal(
230
+ SlotNumber(1),
231
+ payloadHashForSlot1,
232
+ );
183
233
  expect(attestationsForSlot1.length).toBe(signers.length);
184
234
 
185
- await ap.deleteCheckpointAttestationsOlderThan(SlotNumber(73));
235
+ await ap.deleteOlderThan(SlotNumber(73));
186
236
 
187
237
  const attestationsForSlot1AfterDelete = await ap.getCheckpointAttestationsForSlotAndProposal(
188
238
  SlotNumber(1),
189
- proposalId,
239
+ payloadHashForSlot1,
190
240
  );
191
241
  expect(attestationsForSlot1AfterDelete.length).toBe(0);
192
242
  });
@@ -197,121 +247,122 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
197
247
  const slotNumber = 420;
198
248
  const archive = Fr.random();
199
249
  const proposal = await mockBlockProposalForPool(signers[0], slotNumber, archive);
200
- const proposalId = proposal.archive.toString();
201
250
 
202
- await ap.addBlockProposal(proposal);
251
+ const result = await ap.tryAddBlockProposal(proposal);
252
+
253
+ expect(result.added).toBe(true);
254
+ expect(result.alreadyExists).toBe(false);
255
+ expect(result.count).toBe(1);
203
256
 
204
- const retrievedProposal = await ap.getBlockProposal(proposalId);
257
+ const retrievedProposal = await ap.getBlockProposalByArchive(proposal.archive.toString());
205
258
 
206
259
  expect(retrievedProposal).toBeDefined();
207
260
  expect(retrievedProposal!).toEqual(proposal);
208
-
209
- // Check hasBlockProposal with both id and object
210
- expect(await ap.hasBlockProposal(proposalId)).toBe(true);
211
- expect(await ap.hasBlockProposal(proposal)).toBe(true);
212
261
  });
213
262
 
214
263
  it('should return undefined for non-existent block proposal', async () => {
215
264
  const nonExistentId = Fr.random().toString();
216
- const retrievedProposal = await ap.getBlockProposal(nonExistentId);
265
+ const retrievedProposal = await ap.getBlockProposalByArchive(nonExistentId);
217
266
  expect(retrievedProposal).toBeUndefined();
218
-
219
- // Check hasBlockProposal returns false for non-existent proposal
220
- expect(await ap.hasBlockProposal(nonExistentId)).toBe(false);
221
267
  });
222
268
 
223
- it('should update block proposal if added twice with same id', async () => {
269
+ it('should return alreadyExists when re-adding the same signed payload', async () => {
224
270
  const slotNumber = 420;
225
271
  const archive = Fr.random();
226
- const proposal1 = await mockBlockProposalForPool(signers[0], slotNumber, archive);
227
- const proposalId = proposal1.archive.toString();
228
-
229
- await ap.addBlockProposal(proposal1);
272
+ const proposal = await mockBlockProposalForPool(signers[0], slotNumber, archive);
230
273
 
231
- // Create a new proposal with same archive but different signer
232
- const proposal2 = await mockBlockProposalForPool(signers[1], slotNumber, archive);
274
+ const result1 = await ap.tryAddBlockProposal(proposal);
275
+ expect(result1.added).toBe(true);
276
+ expect(result1.alreadyExists).toBe(false);
233
277
 
234
- await ap.addBlockProposal(proposal2);
278
+ // Re-broadcasting the exact same proposal yields alreadyExists.
279
+ const result2 = await ap.tryAddBlockProposal(proposal);
280
+ expect(result2.added).toBe(false);
281
+ expect(result2.alreadyExists).toBe(true);
235
282
 
236
- const retrievedProposal = await ap.getBlockProposal(proposalId);
283
+ const retrievedProposal = await ap.getBlockProposalByArchive(proposal.archive.toString());
237
284
  expect(retrievedProposal).toBeDefined();
238
- // Should have the second proposal
239
- expect(retrievedProposal!.toBuffer()).toEqual(proposal2.toBuffer());
240
- expect(retrievedProposal!.getSender()?.toString()).toBe(signers[1].address.toString());
285
+ expect(retrievedProposal!.toBuffer()).toEqual(proposal.toBuffer());
286
+ expect(retrievedProposal!.getSender()?.toString()).toBe(signers[0].address.toString());
241
287
  });
242
288
 
243
- it('should handle block proposals with different slots and same archive', async () => {
244
- const archive = Fr.random();
245
- const proposal1 = await mockBlockProposalForPool(signers[0], 100, archive);
246
- const proposal2 = await mockBlockProposalForPool(signers[1], 200, archive);
247
- const proposalId = archive.toString();
289
+ it('should retain an exact duplicate block proposal only once', async () => {
290
+ const slotNumber = 420;
291
+ const proposal = await mockBlockProposalForPool(signers[0], slotNumber);
248
292
 
249
- await ap.addBlockProposal(proposal1);
250
- await ap.addBlockProposal(proposal2);
293
+ await ap.tryAddBlockProposal(proposal);
294
+ await ap.tryAddBlockProposal(proposal);
251
295
 
252
- // Should get the latest one added
253
- const retrievedProposal = await ap.getBlockProposal(proposalId);
254
- expect(retrievedProposal).toBeDefined();
255
- expect(retrievedProposal!.toBuffer()).toEqual(proposal2.toBuffer());
256
- expect(retrievedProposal!.slotNumber).toBe(SlotNumber(200));
296
+ const proposals = await ap.getProposalsForSlot(SlotNumber(slotNumber));
297
+ expect(proposals.blockProposals.map(proposal => proposal.toBuffer())).toEqual([
298
+ proposal.withoutSignedTxs().toBuffer(),
299
+ ]);
300
+ });
301
+
302
+ it('should retain all accepted block proposals at a position', async () => {
303
+ const slotNumber = 420;
304
+ const blockHeader = makeBlockHeader(1, { slotNumber: SlotNumber(slotNumber) });
305
+ const proposal1 = await makeBlockProposal({
306
+ signer: signers[0],
307
+ blockHeader,
308
+ archiveRoot: Fr.random(),
309
+ indexWithinCheckpoint: IndexWithinCheckpoint(1),
310
+ });
311
+ const proposal2 = await makeBlockProposal({
312
+ signer: signers[0],
313
+ blockHeader,
314
+ archiveRoot: Fr.random(),
315
+ indexWithinCheckpoint: IndexWithinCheckpoint(1),
316
+ });
317
+
318
+ await ap.tryAddBlockProposal(proposal1);
319
+ await ap.tryAddBlockProposal(proposal2);
320
+
321
+ const proposals = await ap.getProposalsForSlot(SlotNumber(slotNumber));
322
+ expect(proposals.blockProposals.map(proposal => proposal.toBuffer())).toEqual(
323
+ expect.arrayContaining([proposal1.withoutSignedTxs().toBuffer(), proposal2.withoutSignedTxs().toBuffer()]),
324
+ );
325
+ expect(await ap.getBlockProposalByArchive(proposal2.archive.toString())).toBeDefined();
257
326
  });
258
327
  });
259
328
 
260
329
  describe('CheckpointProposal in attestation pool', () => {
261
- const mockCheckpointProposalForPool = (
330
+ const mockCheckpointProposalForPool = async (
262
331
  signer: Secp256k1Signer,
263
332
  slotNumber: number,
264
333
  archive: Fr = Fr.random(),
265
- ): Promise<CheckpointProposal> => {
266
- const checkpointHeader = makeCheckpointHeader(1, { slotNumber: SlotNumber(slotNumber) });
334
+ checkpointHeader?: CheckpointHeader,
335
+ ): Promise<CheckpointProposalCore> => {
336
+ const headerToUse = checkpointHeader ?? makeCheckpointHeader(1, { slotNumber: SlotNumber(slotNumber) });
267
337
  const blockHeader = makeBlockHeader(1);
268
- return makeCheckpointProposal({
338
+ const proposal = await makeCheckpointProposal({
269
339
  signer,
270
- checkpointHeader,
340
+ checkpointHeader: headerToUse,
271
341
  archiveRoot: archive,
272
342
  lastBlock: { blockHeader },
273
343
  });
344
+ // Return the core version since tryAddCheckpointProposal now takes CheckpointProposalCore
345
+ return proposal.toCore();
274
346
  };
275
347
 
276
- it('should add and retrieve checkpoint proposal as core (without lastBlock)', async () => {
348
+ it('should add and retrieve checkpoint proposal', async () => {
277
349
  const slotNumber = 420;
278
350
  const archive = Fr.random();
279
351
  const proposal = await mockCheckpointProposalForPool(signers[0], slotNumber, archive);
280
- const proposalId = proposal.archive.toString();
281
-
282
- await ap.addCheckpointProposal(proposal);
283
-
284
- const retrievedProposal = await ap.getCheckpointProposal(proposalId);
285
352
 
286
- expect(retrievedProposal).toBeDefined();
287
- // Should return core version (without lastBlock)
288
- expect(retrievedProposal!.toBuffer()).toEqual(proposal.toCore().toBuffer());
353
+ const result = await ap.tryAddCheckpointProposal(proposal);
289
354
 
290
- // Check hasCheckpointProposal with both id and object
291
- expect(await ap.hasCheckpointProposal(proposalId)).toBe(true);
292
- expect(await ap.hasCheckpointProposal(proposal)).toBe(true);
293
- });
355
+ expect(result.added).toBe(true);
356
+ expect(result.alreadyExists).toBe(false);
357
+ expect(result.count).toBe(1);
294
358
 
295
- it('should extract and store block proposal when adding checkpoint proposal with lastBlock', async () => {
296
- const slotNumber = 420;
297
- const archive = Fr.random();
298
- const proposal = await mockCheckpointProposalForPool(signers[0], slotNumber, archive);
299
- const proposalId = proposal.archive.toString();
359
+ const retrievedProposal = await ap.getCheckpointProposal(SlotNumber(slotNumber));
300
360
 
301
- // Verify the proposal has a lastBlock
302
- const expectedBlockProposal = proposal.getBlockProposal();
303
- expect(expectedBlockProposal).toBeDefined();
304
-
305
- await ap.addCheckpointProposal(proposal);
306
-
307
- // The block proposal should be stored separately and retrievable
308
- const retrievedBlockProposal = await ap.getBlockProposal(proposalId);
309
- expect(retrievedBlockProposal).toBeDefined();
310
- expect(retrievedBlockProposal!.archive.toString()).toBe(archive.toString());
311
- expect(retrievedBlockProposal!.blockHeader.toBuffer()).toEqual(expectedBlockProposal!.blockHeader.toBuffer());
361
+ expect(retrievedProposal).toBeDefined();
362
+ expect(retrievedProposal!.toBuffer()).toEqual(proposal.toBuffer());
312
363
  });
313
364
 
314
- it('should not store block proposal when checkpoint proposal has no lastBlock', async () => {
365
+ it('should handle checkpoint proposal without lastBlock (caller extracts and adds block separately)', async () => {
315
366
  const slotNumber = 420;
316
367
  const archive = Fr.random();
317
368
  const checkpointHeader = makeCheckpointHeader(1, { slotNumber: SlotNumber(slotNumber) });
@@ -322,60 +373,524 @@ export function describeAttestationPool(getAttestationPool: () => AttestationPoo
322
373
  archiveRoot: archive,
323
374
  // No lastBlock
324
375
  });
325
- const proposalId = proposal.archive.toString();
326
376
 
327
- await ap.addCheckpointProposal(proposal);
377
+ // Add the checkpoint core - block extraction is now caller responsibility
378
+ await ap.tryAddCheckpointProposal(proposal.toCore());
328
379
 
329
380
  // The checkpoint proposal should be stored
330
- const retrievedCheckpointProposal = await ap.getCheckpointProposal(proposalId);
381
+ const retrievedCheckpointProposal = await ap.getCheckpointProposal(SlotNumber(slotNumber));
331
382
  expect(retrievedCheckpointProposal).toBeDefined();
332
383
 
333
- // But no block proposal should be stored (archive key won't have a block proposal)
334
- const retrievedBlockProposal = await ap.getBlockProposal(proposalId);
384
+ // No block proposal was extracted (it had none anyway)
385
+ const retrievedBlockProposal = await ap.getBlockProposalByArchive(proposal.archive.toString());
335
386
  expect(retrievedBlockProposal).toBeUndefined();
336
387
  });
337
388
 
338
389
  it('should return undefined for non-existent checkpoint proposal', async () => {
339
- const nonExistentId = Fr.random().toString();
340
- const retrievedProposal = await ap.getCheckpointProposal(nonExistentId);
390
+ const retrievedProposal = await ap.getCheckpointProposal(SlotNumber(99999));
341
391
  expect(retrievedProposal).toBeUndefined();
392
+ });
393
+
394
+ it('should return alreadyExists when re-adding the same signed payload', async () => {
395
+ const slotNumber = 420;
396
+ const archive = Fr.random();
397
+ const proposal = await mockCheckpointProposalForPool(signers[0], slotNumber, archive);
398
+
399
+ const result1 = await ap.tryAddCheckpointProposal(proposal);
400
+ expect(result1.added).toBe(true);
401
+ expect(result1.alreadyExists).toBe(false);
402
+
403
+ // Re-broadcasting the exact same signed payload yields alreadyExists.
404
+ const result2 = await ap.tryAddCheckpointProposal(proposal);
405
+ expect(result2.added).toBe(false);
406
+ expect(result2.alreadyExists).toBe(true);
342
407
 
343
- // Check hasCheckpointProposal returns false for non-existent proposal
344
- expect(await ap.hasCheckpointProposal(nonExistentId)).toBe(false);
408
+ // Should still have the first proposal stored at the slot
409
+ const retrievedProposal = await ap.getCheckpointProposal(SlotNumber(slotNumber));
410
+ expect(retrievedProposal).toBeDefined();
411
+ expect(retrievedProposal!.toBuffer()).toEqual(proposal.toBuffer());
412
+ expect(retrievedProposal!.getSender()?.toString()).toBe(signers[0].address.toString());
413
+ });
414
+
415
+ it('should treat distinct payloads at the same slot as equivocations (count = 2)', async () => {
416
+ const slotNumber = 420;
417
+ // Two proposals at the same slot but with different headers (distinct payloads).
418
+ const proposal1 = await mockCheckpointProposalForPool(signers[0], slotNumber, Fr.random());
419
+ const proposal2 = await mockCheckpointProposalForPool(signers[0], slotNumber, Fr.random());
420
+
421
+ const result1 = await ap.tryAddCheckpointProposal(proposal1);
422
+ expect(result1.added).toBe(true);
423
+ expect(result1.count).toBe(1);
424
+
425
+ const result2 = await ap.tryAddCheckpointProposal(proposal2);
426
+ // The second distinct payload is tracked as an equivocation, count goes to 2,
427
+ // and both accepted payloads are retained by payload hash.
428
+ expect(result2.added).toBe(true);
429
+ expect(result2.alreadyExists).toBe(false);
430
+ expect(result2.count).toBe(2);
431
+
432
+ const retrievedProposal = await ap.getCheckpointProposal(SlotNumber(slotNumber));
433
+ const expectedProposal = [proposal1, proposal2].sort((a, b) =>
434
+ a.getPayloadHash().localeCompare(b.getPayloadHash()),
435
+ )[0];
436
+ expect(retrievedProposal!.toBuffer()).toEqual(expectedProposal.toBuffer());
437
+
438
+ const proposals = await ap.getProposalsForSlot(SlotNumber(slotNumber));
439
+ expect(proposals.checkpointProposals.map(proposal => proposal.toBuffer())).toEqual(
440
+ expect.arrayContaining([proposal1.toBuffer(), proposal2.toBuffer()]),
441
+ );
345
442
  });
346
443
 
347
- it('should update checkpoint proposal if added twice with same id', async () => {
444
+ it('should detect equivocation when only feeAssetPriceModifier differs', async () => {
348
445
  const slotNumber = 420;
349
446
  const archive = Fr.random();
350
- const proposal1 = await mockCheckpointProposalForPool(signers[0], slotNumber, archive);
351
- const proposalId = proposal1.archive.toString();
447
+ // Same checkpoint header + archive, but two different feeAssetPriceModifier values.
448
+ // This is the audit-finding scenario: archive collides but the signed payload differs.
449
+ const sharedHeader = makeCheckpointHeader(1, { slotNumber: SlotNumber(slotNumber) });
450
+ const proposalA = await makeCheckpointProposal({
451
+ signer: signers[0],
452
+ checkpointHeader: sharedHeader,
453
+ archiveRoot: archive,
454
+ feeAssetPriceModifier: 50n,
455
+ });
456
+ const proposalB = await makeCheckpointProposal({
457
+ signer: signers[0],
458
+ checkpointHeader: sharedHeader,
459
+ archiveRoot: archive,
460
+ feeAssetPriceModifier: -50n,
461
+ });
352
462
 
353
- await ap.addCheckpointProposal(proposal1);
463
+ const result1 = await ap.tryAddCheckpointProposal(proposalA.toCore());
464
+ expect(result1.count).toBe(1);
354
465
 
355
- // Create a new proposal with same archive but different signer
356
- const proposal2 = await mockCheckpointProposalForPool(signers[1], slotNumber, archive);
466
+ const result2 = await ap.tryAddCheckpointProposal(proposalB.toCore());
467
+ // The fix: archive collision no longer hides the equivocation; payload-hash dedup
468
+ // sees the distinct feeMod and bumps `count` to 2 so libp2p can fire the slash callback.
469
+ expect(result2.added).toBe(true);
470
+ expect(result2.alreadyExists).toBe(false);
471
+ expect(result2.count).toBe(2);
472
+ });
357
473
 
358
- await ap.addCheckpointProposal(proposal2);
474
+ it('should delete retained proposals older than a given slot', async () => {
475
+ const oldSlot = 100;
476
+ const newSlot = 200;
477
+ const oldBlock = await mockBlockProposalForPool(signers[0], oldSlot);
478
+ const newBlock = await mockBlockProposalForPool(signers[1], newSlot);
479
+ const oldCheckpoint = await mockCheckpointProposalForPool(signers[0], oldSlot);
480
+ const newCheckpoint = await mockCheckpointProposalForPool(signers[1], newSlot);
359
481
 
360
- const retrievedProposal = await ap.getCheckpointProposal(proposalId);
361
- expect(retrievedProposal).toBeDefined();
362
- // Should have the second proposal (as core)
363
- expect(retrievedProposal!.toBuffer()).toEqual(proposal2.toCore().toBuffer());
364
- expect(retrievedProposal!.getSender()?.toString()).toBe(signers[1].address.toString());
482
+ await ap.tryAddBlockProposal(oldBlock);
483
+ await ap.tryAddBlockProposal(newBlock);
484
+ await ap.tryAddCheckpointProposal(oldCheckpoint);
485
+ await ap.tryAddCheckpointProposal(newCheckpoint);
486
+
487
+ await ap.deleteOlderThan(SlotNumber(newSlot));
488
+
489
+ expect(await ap.getProposalsForSlot(SlotNumber(oldSlot))).toEqual({
490
+ blockProposals: [],
491
+ checkpointProposals: [],
492
+ });
493
+ const newProposals = await ap.getProposalsForSlot(SlotNumber(newSlot));
494
+ expect(newProposals.blockProposals.map(proposal => proposal.toBuffer())).toContainEqual(
495
+ newBlock.withoutSignedTxs().toBuffer(),
496
+ );
497
+ expect(newProposals.checkpointProposals.map(proposal => proposal.toBuffer())).toContainEqual(
498
+ newCheckpoint.toBuffer(),
499
+ );
365
500
  });
366
501
 
367
- it('should throw ProposalSlotCapExceededError when exceeding capacity', async () => {
502
+ it('should return added=false when exceeding capacity', async () => {
368
503
  const slotNumber = 420;
369
504
 
370
- // Add MAX_PROPOSALS_PER_SLOT proposals
371
- for (let i = 0; i < MAX_PROPOSALS_PER_SLOT; i++) {
505
+ // Add MAX_CHECKPOINT_PROPOSALS_PER_SLOT distinct proposals.
506
+ for (let i = 0; i < MAX_CHECKPOINT_PROPOSALS_PER_SLOT; i++) {
372
507
  const proposal = await mockCheckpointProposalForPool(signers[i % NUMBER_OF_SIGNERS_PER_TEST], slotNumber);
373
- await ap.addCheckpointProposal(proposal);
508
+ const result = await ap.tryAddCheckpointProposal(proposal);
509
+ expect(result.added).toBe(true);
510
+ expect(result.count).toBe(i + 1);
374
511
  }
375
512
 
376
- // The next proposal should throw
513
+ // The next proposal should not be added.
377
514
  const extraProposal = await mockCheckpointProposalForPool(signers[0], slotNumber);
378
- await expect(ap.addCheckpointProposal(extraProposal)).rejects.toThrow('Maximum checkpoint proposals per slot');
515
+ const result = await ap.tryAddCheckpointProposal(extraProposal);
516
+ expect(result.added).toBe(false);
517
+ expect(result.alreadyExists).toBe(false);
518
+ expect(result.count).toBe(MAX_CHECKPOINT_PROPOSALS_PER_SLOT);
519
+ });
520
+ });
521
+
522
+ describe('Duplicate proposal detection', () => {
523
+ const mockBlockProposalWithIndex = (
524
+ signer: Secp256k1Signer,
525
+ slotNumber: number,
526
+ indexWithinCheckpoint: number,
527
+ archive: Fr = Fr.random(),
528
+ ): Promise<BlockProposal> => {
529
+ const header = makeBlockHeader(1, { slotNumber: SlotNumber(slotNumber) });
530
+ return makeBlockProposal({
531
+ signer,
532
+ blockHeader: header,
533
+ archiveRoot: archive,
534
+ indexWithinCheckpoint: IndexWithinCheckpoint(indexWithinCheckpoint),
535
+ });
536
+ };
537
+
538
+ describe('tryAddBlockProposal duplicate detection', () => {
539
+ it('should return count=1 when pool is empty', async () => {
540
+ const proposal = await mockBlockProposalWithIndex(signers[0], 100, 0);
541
+ const result = await ap.tryAddBlockProposal(proposal);
542
+
543
+ expect(result.added).toBe(true);
544
+ expect(result.alreadyExists).toBe(false);
545
+ expect(result.count).toBe(1);
546
+ });
547
+
548
+ it('should return alreadyExists when same proposal exists', async () => {
549
+ const proposal = await mockBlockProposalWithIndex(signers[0], 100, 0);
550
+ await ap.tryAddBlockProposal(proposal);
551
+
552
+ const result = await ap.tryAddBlockProposal(proposal);
553
+
554
+ expect(result.added).toBe(false);
555
+ expect(result.alreadyExists).toBe(true);
556
+ expect(result.count).toBe(1);
557
+ });
558
+
559
+ it('should detect duplicate via count when different proposal exists at same position', async () => {
560
+ const slotNumber = 100;
561
+ const indexWithinCheckpoint = 2;
562
+
563
+ // Add first proposal
564
+ const proposal1 = await mockBlockProposalWithIndex(signers[0], slotNumber, indexWithinCheckpoint);
565
+ const result1 = await ap.tryAddBlockProposal(proposal1);
566
+ expect(result1.count).toBe(1);
567
+
568
+ // Add a different proposal at same position - this is a duplicate (equivocation)
569
+ const proposal2 = await mockBlockProposalWithIndex(signers[1], slotNumber, indexWithinCheckpoint);
570
+ const result2 = await ap.tryAddBlockProposal(proposal2);
571
+
572
+ expect(result2.added).toBe(true);
573
+ expect(result2.alreadyExists).toBe(false);
574
+ // count >= 2 indicates duplicate detection
575
+ expect(result2.count).toBe(2);
576
+ });
577
+
578
+ it('should not detect duplicate for different positions in same slot', async () => {
579
+ const slotNumber = 100;
580
+
581
+ // Add proposal at index 0
582
+ const proposal1 = await mockBlockProposalWithIndex(signers[0], slotNumber, 0);
583
+ await ap.tryAddBlockProposal(proposal1);
584
+
585
+ // Add proposal at index 1 (different position)
586
+ const proposal2 = await mockBlockProposalWithIndex(signers[1], slotNumber, 1);
587
+ const result = await ap.tryAddBlockProposal(proposal2);
588
+
589
+ expect(result.added).toBe(true);
590
+ // count = 1 means no duplicate for this position
591
+ expect(result.count).toBe(1);
592
+ });
593
+
594
+ it('should not detect duplicate for same position in different slots', async () => {
595
+ const indexWithinCheckpoint = 0;
596
+
597
+ // Add proposal at slot 100
598
+ const proposal1 = await mockBlockProposalWithIndex(signers[0], 100, indexWithinCheckpoint);
599
+ await ap.tryAddBlockProposal(proposal1);
600
+
601
+ // Add proposal at slot 200 (different slot)
602
+ const proposal2 = await mockBlockProposalWithIndex(signers[1], 200, indexWithinCheckpoint);
603
+ const result = await ap.tryAddBlockProposal(proposal2);
604
+
605
+ expect(result.added).toBe(true);
606
+ // count = 1 means no duplicate for this position
607
+ expect(result.count).toBe(1);
608
+ });
609
+
610
+ it('should track multiple duplicates correctly via count', async () => {
611
+ const slotNumber = 100;
612
+ const indexWithinCheckpoint = 0;
613
+
614
+ // Add multiple proposals for same position
615
+ const proposal1 = await mockBlockProposalWithIndex(signers[0], slotNumber, indexWithinCheckpoint);
616
+ const result1 = await ap.tryAddBlockProposal(proposal1);
617
+ expect(result1.count).toBe(1);
618
+
619
+ const proposal2 = await mockBlockProposalWithIndex(signers[1], slotNumber, indexWithinCheckpoint);
620
+ const result2 = await ap.tryAddBlockProposal(proposal2);
621
+ expect(result2.count).toBe(2);
622
+
623
+ // Third proposal for same position should be rejected (cap is 2)
624
+ const proposal3 = await mockBlockProposalWithIndex(signers[2], slotNumber, indexWithinCheckpoint);
625
+ const result3 = await ap.tryAddBlockProposal(proposal3);
626
+
627
+ expect(result3.added).toBe(false);
628
+ expect(result3.count).toBe(2);
629
+ });
630
+
631
+ it('should return added=false when exceeding capacity', async () => {
632
+ const slotNumber = 100;
633
+ const indexWithinCheckpoint = 0;
634
+
635
+ // Add MAX_BLOCK_PROPOSALS_PER_POSITION proposals
636
+ for (let i = 0; i < MAX_BLOCK_PROPOSALS_PER_POSITION; i++) {
637
+ const proposal = await mockBlockProposalWithIndex(
638
+ signers[i % NUMBER_OF_SIGNERS_PER_TEST],
639
+ slotNumber,
640
+ indexWithinCheckpoint,
641
+ );
642
+ const result = await ap.tryAddBlockProposal(proposal);
643
+ expect(result.added).toBe(true);
644
+ expect(result.count).toBe(i + 1);
645
+ }
646
+
647
+ // The next proposal should not be added
648
+ const extraProposal = await mockBlockProposalWithIndex(signers[0], slotNumber, indexWithinCheckpoint);
649
+ const result = await ap.tryAddBlockProposal(extraProposal);
650
+ expect(result.added).toBe(false);
651
+ expect(result.alreadyExists).toBe(false);
652
+ expect(result.count).toBe(MAX_BLOCK_PROPOSALS_PER_POSITION);
653
+ });
654
+
655
+ it('should clean up block position index when deleting old data', async () => {
656
+ const slotNumber = 100;
657
+ const indexWithinCheckpoint = 0;
658
+
659
+ // Add proposal
660
+ const proposal1 = await mockBlockProposalWithIndex(signers[0], slotNumber, indexWithinCheckpoint);
661
+ await ap.tryAddBlockProposal(proposal1);
662
+
663
+ // Verify it's tracked (adding another should show count = 2)
664
+ const proposal2 = await mockBlockProposalWithIndex(signers[1], slotNumber, indexWithinCheckpoint);
665
+ let result = await ap.tryAddBlockProposal(proposal2);
666
+ expect(result.count).toBe(2);
667
+
668
+ // Delete old data
669
+ await ap.deleteOlderThan(SlotNumber(slotNumber + 1));
670
+
671
+ // Verify position index is cleaned up (count should be 1 now)
672
+ const proposal3 = await mockBlockProposalWithIndex(signers[2], slotNumber, indexWithinCheckpoint);
673
+ result = await ap.tryAddBlockProposal(proposal3);
674
+ expect(result.count).toBe(1);
675
+ });
676
+
677
+ it('should correctly delete block proposals at slot boundary', async () => {
678
+ // Add proposals at slots 99, 100, and 101 with various indices
679
+ const proposalSlot99Idx0 = await mockBlockProposalWithIndex(signers[0], 99, 0);
680
+ const proposalSlot99Idx1 = await mockBlockProposalWithIndex(signers[1], 99, 1);
681
+ const proposalSlot100Idx0 = await mockBlockProposalWithIndex(signers[2], 100, 0);
682
+ const proposalSlot101Idx0 = await mockBlockProposalWithIndex(signers[3], 101, 0);
683
+
684
+ await ap.tryAddBlockProposal(proposalSlot99Idx0);
685
+ await ap.tryAddBlockProposal(proposalSlot99Idx1);
686
+ await ap.tryAddBlockProposal(proposalSlot100Idx0);
687
+ await ap.tryAddBlockProposal(proposalSlot101Idx0);
688
+
689
+ // Delete slots older than 100 (should delete slot 99 only)
690
+ await ap.deleteOlderThan(SlotNumber(100));
691
+
692
+ // Slot 99 proposals should have their index cleaned up
693
+ const newProposal99 = await mockBlockProposalWithIndex(signers[0], 99, 0);
694
+ const result99 = await ap.tryAddBlockProposal(newProposal99);
695
+ expect(result99.count).toBe(1); // Index was cleaned up
696
+
697
+ // Slot 100 and 101 should still be tracked
698
+ const newProposal100 = await mockBlockProposalWithIndex(signers[1], 100, 0);
699
+ const result100 = await ap.tryAddBlockProposal(newProposal100);
700
+ expect(result100.count).toBe(2); // Still has the original
701
+
702
+ const newProposal101 = await mockBlockProposalWithIndex(signers[2], 101, 0);
703
+ const result101 = await ap.tryAddBlockProposal(newProposal101);
704
+ expect(result101.count).toBe(2); // Still has the original
705
+ });
706
+
707
+ it('should delete all indices for a given slot', async () => {
708
+ const slotNumber = 50;
709
+
710
+ // Add proposals at multiple indices for the same slot
711
+ const proposal0 = await mockBlockProposalWithIndex(signers[0], slotNumber, 0);
712
+ const proposal1 = await mockBlockProposalWithIndex(signers[1], slotNumber, 1);
713
+ const proposal2 = await mockBlockProposalWithIndex(signers[2], slotNumber, 2);
714
+
715
+ await ap.tryAddBlockProposal(proposal0);
716
+ await ap.tryAddBlockProposal(proposal1);
717
+ await ap.tryAddBlockProposal(proposal2);
718
+
719
+ // Delete slots older than slotNumber + 1
720
+ await ap.deleteOlderThan(SlotNumber(slotNumber + 1));
721
+
722
+ // All indices should be cleaned up
723
+ const newProposal0 = await mockBlockProposalWithIndex(signers[0], slotNumber, 0);
724
+ const result0 = await ap.tryAddBlockProposal(newProposal0);
725
+ expect(result0.count).toBe(1);
726
+
727
+ const newProposal1 = await mockBlockProposalWithIndex(signers[1], slotNumber, 1);
728
+ const result1 = await ap.tryAddBlockProposal(newProposal1);
729
+ expect(result1.count).toBe(1);
730
+
731
+ const newProposal2 = await mockBlockProposalWithIndex(signers[2], slotNumber, 2);
732
+ const result2 = await ap.tryAddBlockProposal(newProposal2);
733
+ expect(result2.count).toBe(1);
734
+ });
735
+
736
+ it('should delete block proposals from storage when deleting old data', async () => {
737
+ const oldSlot = 50;
738
+ const newSlot = 100;
739
+
740
+ // Add proposals at old and new slots
741
+ const oldProposal = await mockBlockProposalWithIndex(signers[0], oldSlot, 0);
742
+ const newProposal = await mockBlockProposalWithIndex(signers[1], newSlot, 0);
743
+
744
+ await ap.tryAddBlockProposal(oldProposal);
745
+ await ap.tryAddBlockProposal(newProposal);
746
+
747
+ // Verify both proposals exist
748
+ expect(await ap.getBlockProposalByArchive(oldProposal.archive.toString())).toBeDefined();
749
+ expect(await ap.getBlockProposalByArchive(newProposal.archive.toString())).toBeDefined();
750
+
751
+ // Delete slots older than newSlot (should delete oldSlot)
752
+ await ap.deleteOlderThan(SlotNumber(newSlot));
753
+
754
+ // Old proposal should be deleted from storage
755
+ expect(await ap.getBlockProposalByArchive(oldProposal.archive.toString())).toBeUndefined();
756
+
757
+ // New proposal should still exist
758
+ expect(await ap.getBlockProposalByArchive(newProposal.archive.toString())).toBeDefined();
759
+ });
760
+ });
761
+
762
+ describe('tryAddCheckpointProposal duplicate detection', () => {
763
+ const mockCheckpointProposalCoreForPool = async (
764
+ signer: Secp256k1Signer,
765
+ slotNumber: number,
766
+ archive: Fr = Fr.random(),
767
+ checkpointHeader?: CheckpointHeader,
768
+ ): Promise<CheckpointProposalCore> => {
769
+ const headerToUse = checkpointHeader ?? makeCheckpointHeader(1, { slotNumber: SlotNumber(slotNumber) });
770
+ const blockHeader = makeBlockHeader(1);
771
+ const proposal = await makeCheckpointProposal({
772
+ signer,
773
+ checkpointHeader: headerToUse,
774
+ archiveRoot: archive,
775
+ lastBlock: { blockHeader },
776
+ });
777
+ return proposal.toCore();
778
+ };
779
+
780
+ it('should return count=1 when pool is empty', async () => {
781
+ const proposal = await mockCheckpointProposalCoreForPool(signers[0], 100);
782
+ const result = await ap.tryAddCheckpointProposal(proposal);
783
+
784
+ expect(result.added).toBe(true);
785
+ expect(result.alreadyExists).toBe(false);
786
+ expect(result.count).toBe(1);
787
+ });
788
+
789
+ it('should return alreadyExists when same proposal exists', async () => {
790
+ const proposal = await mockCheckpointProposalCoreForPool(signers[0], 100);
791
+ await ap.tryAddCheckpointProposal(proposal);
792
+
793
+ const result = await ap.tryAddCheckpointProposal(proposal);
794
+
795
+ expect(result.added).toBe(false);
796
+ expect(result.alreadyExists).toBe(true);
797
+ expect(result.count).toBe(1);
798
+ });
799
+
800
+ it('should detect duplicate via count when different proposal exists for same slot', async () => {
801
+ const slotNumber = 100;
802
+
803
+ // Add first proposal
804
+ const proposal1 = await mockCheckpointProposalCoreForPool(signers[0], slotNumber);
805
+ const result1 = await ap.tryAddCheckpointProposal(proposal1);
806
+ expect(result1.count).toBe(1);
807
+
808
+ // Add a different proposal for same slot - this is a duplicate (equivocation)
809
+ const proposal2 = await mockCheckpointProposalCoreForPool(signers[1], slotNumber);
810
+ const result2 = await ap.tryAddCheckpointProposal(proposal2);
811
+
812
+ expect(result2.added).toBe(true);
813
+ expect(result2.alreadyExists).toBe(false);
814
+ // count >= 2 indicates duplicate detection
815
+ expect(result2.count).toBe(2);
816
+ });
817
+
818
+ it('should not detect duplicate for different slots', async () => {
819
+ // Add proposal at slot 100
820
+ const proposal1 = await mockCheckpointProposalCoreForPool(signers[0], 100);
821
+ await ap.tryAddCheckpointProposal(proposal1);
822
+
823
+ // Add proposal at slot 200 (different slot)
824
+ const proposal2 = await mockCheckpointProposalCoreForPool(signers[1], 200);
825
+ const result = await ap.tryAddCheckpointProposal(proposal2);
826
+
827
+ expect(result.added).toBe(true);
828
+ // count = 1 means no duplicate for this slot
829
+ expect(result.count).toBe(1);
830
+ });
831
+
832
+ it('should track multiple duplicates correctly via count', async () => {
833
+ const slotNumber = 100;
834
+
835
+ // Add multiple proposals for same slot
836
+ const proposal1 = await mockCheckpointProposalCoreForPool(signers[0], slotNumber);
837
+ const result1 = await ap.tryAddCheckpointProposal(proposal1);
838
+ expect(result1.count).toBe(1);
839
+
840
+ const proposal2 = await mockCheckpointProposalCoreForPool(signers[1], slotNumber);
841
+ const result2 = await ap.tryAddCheckpointProposal(proposal2);
842
+ expect(result2.count).toBe(2);
843
+
844
+ // Third proposal for same slot should be rejected (cap is 2)
845
+ const proposal3 = await mockCheckpointProposalCoreForPool(signers[2], slotNumber);
846
+ const result3 = await ap.tryAddCheckpointProposal(proposal3);
847
+
848
+ expect(result3.added).toBe(false);
849
+ expect(result3.count).toBe(2);
850
+ });
851
+
852
+ it('should not count attestations as proposals for duplicate detection', async () => {
853
+ const slotNumber = 100;
854
+ const archive = Fr.random();
855
+
856
+ // Attestation arrives BEFORE the checkpoint proposal (race condition in p2p)
857
+ const attestation = mockCheckpointAttestation(signers[0], slotNumber, archive);
858
+ await ap.addOwnCheckpointAttestations([attestation]);
859
+
860
+ // Now the checkpoint proposal arrives - this should NOT be detected as a duplicate
861
+ const proposal = await mockCheckpointProposalCoreForPool(signers[1], slotNumber, archive);
862
+ const result = await ap.tryAddCheckpointProposal(proposal);
863
+
864
+ expect(result.added).toBe(true);
865
+ expect(result.alreadyExists).toBe(false);
866
+ // count should be 1, NOT 2 - attestations should not count as proposals
867
+ expect(result.count).toBe(1);
868
+ });
869
+
870
+ it('should not count attestations for different proposals as duplicates', async () => {
871
+ const slotNumber = 100;
872
+ const archive1 = Fr.random();
873
+ const archive2 = Fr.random();
874
+
875
+ // Add attestations for two different proposals in the same slot
876
+ const attestation1 = mockCheckpointAttestation(signers[0], slotNumber, archive1);
877
+ const attestation2 = mockCheckpointAttestation(signers[1], slotNumber, archive2);
878
+ await ap.addOwnCheckpointAttestations([attestation1, attestation2]);
879
+
880
+ // Add the first checkpoint proposal - should not be affected by attestations
881
+ const proposal1 = await mockCheckpointProposalCoreForPool(signers[2], slotNumber, archive1);
882
+ const result1 = await ap.tryAddCheckpointProposal(proposal1);
883
+
884
+ expect(result1.added).toBe(true);
885
+ expect(result1.count).toBe(1);
886
+
887
+ // Add the second checkpoint proposal - this IS a duplicate (different archive, same slot)
888
+ const proposal2 = await mockCheckpointProposalCoreForPool(signers[3], slotNumber, archive2);
889
+ const result2 = await ap.tryAddCheckpointProposal(proposal2);
890
+
891
+ expect(result2.added).toBe(true);
892
+ expect(result2.count).toBe(2);
893
+ });
379
894
  });
380
895
  });
381
896
  }