@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,9 +1,11 @@
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 { Signature } from '@aztec/foundation/eth-signature';
5
+ import { CheckpointAttestation } from '@aztec/stdlib/p2p';
4
6
  import { CheckpointHeader } from '@aztec/stdlib/rollup';
5
7
  import { makeBlockHeader, makeBlockProposal, makeCheckpointHeader, makeCheckpointProposal } from '@aztec/stdlib/testing';
6
- import { MAX_PROPOSALS_PER_SLOT } from './kv_attestation_pool.js';
8
+ import { MAX_BLOCK_PROPOSALS_PER_POSITION, MAX_CHECKPOINT_PROPOSALS_PER_SLOT } from './attestation_pool.js';
7
9
  import { mockCheckpointAttestation } from './mocks.js';
8
10
  const NUMBER_OF_SIGNERS_PER_TEST = 4;
9
11
  export function describeAttestationPool(getAttestationPool) {
@@ -15,9 +17,17 @@ export function describeAttestationPool(getAttestationPool) {
15
17
  length: NUMBER_OF_SIGNERS_PER_TEST
16
18
  }, ()=>Secp256k1Signer.random());
17
19
  });
18
- const createCheckpointAttestationsForSlot = (slotNumber, archive)=>{
20
+ /**
21
+ * Build attestations from each signer over the *same* signed payload (same header,
22
+ * archive, feeAssetPriceModifier). Required by the new pool, which deduplicates by
23
+ * payload hash and treats attestations from different signers as distinct entries
24
+ * only when the payload itself matches.
25
+ */ const createCheckpointAttestationsForSlot = (slotNumber, archive)=>{
19
26
  const archiveToUse = archive ?? Fr.random();
20
- return signers.map((signer)=>mockCheckpointAttestation(signer, slotNumber, archiveToUse));
27
+ const sharedHeader = CheckpointHeader.random({
28
+ slotNumber: SlotNumber(slotNumber)
29
+ });
30
+ return signers.map((signer)=>mockCheckpointAttestation(signer, slotNumber, archiveToUse, sharedHeader));
21
31
  };
22
32
  const mockBlockProposalForPool = (signer, slotNumber, archive = Fr.random())=>{
23
33
  const header = makeBlockHeader(1, {
@@ -37,34 +47,74 @@ export function describeAttestationPool(getAttestationPool) {
37
47
  expect(a1Buffer.length).toBe(a2Buffer.length);
38
48
  expect(a1Buffer).toEqual(expect.arrayContaining(a2Buffer));
39
49
  };
50
+ /**
51
+ * Rewrites the attester signature into yParity form (27 -> 0, 28 -> 1), keeping (r, s). The recovery
52
+ * bit is unchanged so it still recovers to the same signer, mimicking a peer that gossips (or mutates
53
+ * in flight) a signature with a non-canonical recovery byte.
54
+ */ const toYParityForm = (attestation)=>{
55
+ const sig = attestation.signature;
56
+ const yParityV = sig.v === 27 ? 0 : sig.v === 28 ? 1 : sig.v;
57
+ return new CheckpointAttestation(attestation.payload, new Signature(sig.r, sig.s, yParityV), attestation.proposerSignature);
58
+ };
40
59
  describe('CheckpointAttestation', ()=>{
60
+ it('normalizes yParity (v=0/v=1) attester signatures on ingress from both gossip and own keys', async ()=>{
61
+ const slotNumber = 421;
62
+ const canonical = createCheckpointAttestationsForSlot(slotNumber);
63
+ const payloadHash = canonical[0].getPayloadHash();
64
+ const expectedSenders = new Set([
65
+ canonical[0].getSender().toString(),
66
+ canonical[1].getSender().toString()
67
+ ]);
68
+ const gossiped = toYParityForm(canonical[0]);
69
+ expect([
70
+ 0,
71
+ 1
72
+ ]).toContain(gossiped.signature.v);
73
+ await ap.tryAddCheckpointAttestation(gossiped);
74
+ const own = toYParityForm(canonical[1]);
75
+ expect([
76
+ 0,
77
+ 1
78
+ ]).toContain(own.signature.v);
79
+ await ap.addOwnCheckpointAttestations([
80
+ own
81
+ ]);
82
+ const retrieved = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(slotNumber), payloadHash);
83
+ expect(retrieved.length).toBe(2);
84
+ for (const attestation of retrieved){
85
+ expect([
86
+ 27,
87
+ 28
88
+ ]).toContain(attestation.signature.v);
89
+ expect(expectedSenders.has(attestation.getSender().toString())).toBe(true);
90
+ }
91
+ });
41
92
  it('should add attestations to pool', async ()=>{
42
93
  const slotNumber = 420;
43
94
  const archive = Fr.random();
44
- const attestations = signers.slice(0, -1).map((signer)=>mockCheckpointAttestation(signer, slotNumber, archive));
45
- await ap.addCheckpointAttestations(attestations);
46
- const retrievedAttestations = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(slotNumber), archive.toString());
95
+ const sharedHeader = CheckpointHeader.random({
96
+ slotNumber: SlotNumber(slotNumber)
97
+ });
98
+ const attestations = signers.slice(0, -1).map((signer)=>mockCheckpointAttestation(signer, slotNumber, archive, sharedHeader));
99
+ const payloadHash = attestations[0].getPayloadHash();
100
+ await ap.addOwnCheckpointAttestations(attestations);
101
+ const retrievedAttestations = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(slotNumber), payloadHash);
47
102
  expect(retrievedAttestations.length).toBe(attestations.length);
48
103
  compareCheckpointAttestations(retrievedAttestations, attestations);
49
- // Check hasCheckpointAttestation for added attestations
50
- for (const attestation of attestations){
51
- expect(await ap.hasCheckpointAttestation(attestation)).toBe(true);
52
- }
53
104
  const retrievedAttestationsForSlot = await ap.getCheckpointAttestationsForSlot(SlotNumber(slotNumber));
54
105
  expect(retrievedAttestationsForSlot.length).toBe(attestations.length);
55
106
  compareCheckpointAttestations(retrievedAttestationsForSlot, attestations);
56
107
  // Add another one
57
- const newAttestation = mockCheckpointAttestation(signers[NUMBER_OF_SIGNERS_PER_TEST - 1], slotNumber, archive);
58
- await ap.addCheckpointAttestations([
108
+ const newAttestation = mockCheckpointAttestation(signers[NUMBER_OF_SIGNERS_PER_TEST - 1], slotNumber, archive, sharedHeader);
109
+ await ap.addOwnCheckpointAttestations([
59
110
  newAttestation
60
111
  ]);
61
- const retrievedAttestationsAfterAdd = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(slotNumber), archive.toString());
112
+ const retrievedAttestationsAfterAdd = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(slotNumber), payloadHash);
62
113
  expect(retrievedAttestationsAfterAdd.length).toBe(attestations.length + 1);
63
114
  compareCheckpointAttestations(retrievedAttestationsAfterAdd, [
64
115
  ...attestations,
65
116
  newAttestation
66
117
  ]);
67
- expect(await ap.hasCheckpointAttestation(newAttestation)).toBe(true);
68
118
  const retrievedAttestationsForSlotAfterAdd = await ap.getCheckpointAttestationsForSlot(SlotNumber(slotNumber));
69
119
  expect(retrievedAttestationsForSlotAfterAdd.length).toBe(attestations.length + 1);
70
120
  compareCheckpointAttestations(retrievedAttestationsForSlotAfterAdd, [
@@ -72,14 +122,9 @@ export function describeAttestationPool(getAttestationPool) {
72
122
  newAttestation
73
123
  ]);
74
124
  // Delete by slot
75
- await ap.deleteCheckpointAttestationsOlderThan(SlotNumber(slotNumber + 1));
76
- const retreivedAttestationsAfterDelete = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(slotNumber), archive.toString());
125
+ await ap.deleteOlderThan(SlotNumber(slotNumber + 1));
126
+ const retreivedAttestationsAfterDelete = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(slotNumber), payloadHash);
77
127
  expect(retreivedAttestationsAfterDelete.length).toBe(0);
78
- // Check hasCheckpointAttestation after deletion
79
- for (const attestation of attestations){
80
- expect(await ap.hasCheckpointAttestation(attestation)).toBe(false);
81
- }
82
- expect(await ap.hasCheckpointAttestation(newAttestation)).toBe(false);
83
128
  });
84
129
  it('should handle duplicate proposals in a slot', async ()=>{
85
130
  const slotNumber = 420;
@@ -93,17 +138,18 @@ export function describeAttestationPool(getAttestationPool) {
93
138
  for(let i = 0; i < NUMBER_OF_SIGNERS_PER_TEST; i++){
94
139
  attestations.push(mockCheckpointAttestation(signer, slotNumber, archive, header));
95
140
  }
141
+ const payloadHash = attestations[0].getPayloadHash();
96
142
  // Add them to store and check we end up with only one
97
- await ap.addCheckpointAttestations(attestations);
98
- const retreivedAttestations = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(slotNumber), archive.toString());
143
+ await ap.addOwnCheckpointAttestations(attestations);
144
+ const retreivedAttestations = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(slotNumber), payloadHash);
99
145
  expect(retreivedAttestations.length).toBe(1);
100
146
  expect(retreivedAttestations[0].toBuffer()).toEqual(attestations[0].toBuffer());
101
147
  expect(retreivedAttestations[0].getSender()?.toString()).toEqual(signer.address.toString());
102
148
  // Try adding them on another operation and check they are still not duplicated
103
- await ap.addCheckpointAttestations([
149
+ await ap.addOwnCheckpointAttestations([
104
150
  attestations[0]
105
151
  ]);
106
- expect(await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(slotNumber), archive.toString())).toHaveLength(1);
152
+ expect(await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(slotNumber), payloadHash)).toHaveLength(1);
107
153
  });
108
154
  it('should store attestations by differing slot', async ()=>{
109
155
  const slotNumbers = [
@@ -113,11 +159,11 @@ export function describeAttestationPool(getAttestationPool) {
113
159
  4
114
160
  ];
115
161
  const attestations = signers.map((signer, i)=>mockCheckpointAttestation(signer, slotNumbers[i]));
116
- await ap.addCheckpointAttestations(attestations);
162
+ await ap.addOwnCheckpointAttestations(attestations);
117
163
  for (const attestation of attestations){
118
164
  const slot = attestation.payload.header.slotNumber;
119
- const archive = attestation.archive.toString();
120
- const retreivedAttestations = await ap.getCheckpointAttestationsForSlotAndProposal(slot, archive);
165
+ const payloadHash = attestation.getPayloadHash();
166
+ const retreivedAttestations = await ap.getCheckpointAttestationsForSlotAndProposal(slot, payloadHash);
121
167
  expect(retreivedAttestations.length).toBe(1);
122
168
  expect(retreivedAttestations[0].toBuffer()).toEqual(attestation.toBuffer());
123
169
  expect(retreivedAttestations[0].payload.header.slotNumber).toEqual(slot);
@@ -137,11 +183,11 @@ export function describeAttestationPool(getAttestationPool) {
137
183
  Fr.random()
138
184
  ];
139
185
  const attestations = signers.map((signer, i)=>mockCheckpointAttestation(signer, slotNumbers[i], archives[i]));
140
- await ap.addCheckpointAttestations(attestations);
186
+ await ap.addOwnCheckpointAttestations(attestations);
141
187
  for (const attestation of attestations){
142
188
  const slot = attestation.payload.header.slotNumber;
143
- const proposalId = attestation.archive.toString();
144
- const retreivedAttestations = await ap.getCheckpointAttestationsForSlotAndProposal(slot, proposalId);
189
+ const payloadHash = attestation.getPayloadHash();
190
+ const retreivedAttestations = await ap.getCheckpointAttestationsForSlotAndProposal(slot, payloadHash);
145
191
  expect(retreivedAttestations.length).toBe(1);
146
192
  expect(retreivedAttestations[0].toBuffer()).toEqual(attestation.toBuffer());
147
193
  expect(retreivedAttestations[0].payload.header.slotNumber).toEqual(slot);
@@ -158,13 +204,14 @@ export function describeAttestationPool(getAttestationPool) {
158
204
  88,
159
205
  420
160
206
  ];
161
- const attestations = (await Promise.all(slotNumbers.map((slotNumber)=>createCheckpointAttestationsForSlot(slotNumber)))).flat();
162
- const proposalId = attestations[0].archive.toString();
163
- await ap.addCheckpointAttestations(attestations);
164
- const attestationsForSlot1 = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(1), proposalId);
207
+ const attestationsPerSlot = await Promise.all(slotNumbers.map((slotNumber)=>createCheckpointAttestationsForSlot(slotNumber)));
208
+ const attestations = attestationsPerSlot.flat();
209
+ const payloadHashForSlot1 = attestationsPerSlot[0][0].getPayloadHash();
210
+ await ap.addOwnCheckpointAttestations(attestations);
211
+ const attestationsForSlot1 = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(1), payloadHashForSlot1);
165
212
  expect(attestationsForSlot1.length).toBe(signers.length);
166
- await ap.deleteCheckpointAttestationsOlderThan(SlotNumber(73));
167
- const attestationsForSlot1AfterDelete = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(1), proposalId);
213
+ await ap.deleteOlderThan(SlotNumber(73));
214
+ const attestationsForSlot1AfterDelete = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(1), payloadHashForSlot1);
168
215
  expect(attestationsForSlot1AfterDelete.length).toBe(0);
169
216
  });
170
217
  });
@@ -173,96 +220,102 @@ export function describeAttestationPool(getAttestationPool) {
173
220
  const slotNumber = 420;
174
221
  const archive = Fr.random();
175
222
  const proposal = await mockBlockProposalForPool(signers[0], slotNumber, archive);
176
- const proposalId = proposal.archive.toString();
177
- await ap.addBlockProposal(proposal);
178
- const retrievedProposal = await ap.getBlockProposal(proposalId);
223
+ const result = await ap.tryAddBlockProposal(proposal);
224
+ expect(result.added).toBe(true);
225
+ expect(result.alreadyExists).toBe(false);
226
+ expect(result.count).toBe(1);
227
+ const retrievedProposal = await ap.getBlockProposalByArchive(proposal.archive.toString());
179
228
  expect(retrievedProposal).toBeDefined();
180
229
  expect(retrievedProposal).toEqual(proposal);
181
- // Check hasBlockProposal with both id and object
182
- expect(await ap.hasBlockProposal(proposalId)).toBe(true);
183
- expect(await ap.hasBlockProposal(proposal)).toBe(true);
184
230
  });
185
231
  it('should return undefined for non-existent block proposal', async ()=>{
186
232
  const nonExistentId = Fr.random().toString();
187
- const retrievedProposal = await ap.getBlockProposal(nonExistentId);
233
+ const retrievedProposal = await ap.getBlockProposalByArchive(nonExistentId);
188
234
  expect(retrievedProposal).toBeUndefined();
189
- // Check hasBlockProposal returns false for non-existent proposal
190
- expect(await ap.hasBlockProposal(nonExistentId)).toBe(false);
191
235
  });
192
- it('should update block proposal if added twice with same id', async ()=>{
236
+ it('should return alreadyExists when re-adding the same signed payload', async ()=>{
193
237
  const slotNumber = 420;
194
238
  const archive = Fr.random();
195
- const proposal1 = await mockBlockProposalForPool(signers[0], slotNumber, archive);
196
- const proposalId = proposal1.archive.toString();
197
- await ap.addBlockProposal(proposal1);
198
- // Create a new proposal with same archive but different signer
199
- const proposal2 = await mockBlockProposalForPool(signers[1], slotNumber, archive);
200
- await ap.addBlockProposal(proposal2);
201
- const retrievedProposal = await ap.getBlockProposal(proposalId);
239
+ const proposal = await mockBlockProposalForPool(signers[0], slotNumber, archive);
240
+ const result1 = await ap.tryAddBlockProposal(proposal);
241
+ expect(result1.added).toBe(true);
242
+ expect(result1.alreadyExists).toBe(false);
243
+ // Re-broadcasting the exact same proposal yields alreadyExists.
244
+ const result2 = await ap.tryAddBlockProposal(proposal);
245
+ expect(result2.added).toBe(false);
246
+ expect(result2.alreadyExists).toBe(true);
247
+ const retrievedProposal = await ap.getBlockProposalByArchive(proposal.archive.toString());
202
248
  expect(retrievedProposal).toBeDefined();
203
- // Should have the second proposal
204
- expect(retrievedProposal.toBuffer()).toEqual(proposal2.toBuffer());
205
- expect(retrievedProposal.getSender()?.toString()).toBe(signers[1].address.toString());
249
+ expect(retrievedProposal.toBuffer()).toEqual(proposal.toBuffer());
250
+ expect(retrievedProposal.getSender()?.toString()).toBe(signers[0].address.toString());
206
251
  });
207
- it('should handle block proposals with different slots and same archive', async ()=>{
208
- const archive = Fr.random();
209
- const proposal1 = await mockBlockProposalForPool(signers[0], 100, archive);
210
- const proposal2 = await mockBlockProposalForPool(signers[1], 200, archive);
211
- const proposalId = archive.toString();
212
- await ap.addBlockProposal(proposal1);
213
- await ap.addBlockProposal(proposal2);
214
- // Should get the latest one added
215
- const retrievedProposal = await ap.getBlockProposal(proposalId);
216
- expect(retrievedProposal).toBeDefined();
217
- expect(retrievedProposal.toBuffer()).toEqual(proposal2.toBuffer());
218
- expect(retrievedProposal.slotNumber).toBe(SlotNumber(200));
252
+ it('should retain an exact duplicate block proposal only once', async ()=>{
253
+ const slotNumber = 420;
254
+ const proposal = await mockBlockProposalForPool(signers[0], slotNumber);
255
+ await ap.tryAddBlockProposal(proposal);
256
+ await ap.tryAddBlockProposal(proposal);
257
+ const proposals = await ap.getProposalsForSlot(SlotNumber(slotNumber));
258
+ expect(proposals.blockProposals.map((proposal)=>proposal.toBuffer())).toEqual([
259
+ proposal.withoutSignedTxs().toBuffer()
260
+ ]);
261
+ });
262
+ it('should retain all accepted block proposals at a position', async ()=>{
263
+ const slotNumber = 420;
264
+ const blockHeader = makeBlockHeader(1, {
265
+ slotNumber: SlotNumber(slotNumber)
266
+ });
267
+ const proposal1 = await makeBlockProposal({
268
+ signer: signers[0],
269
+ blockHeader,
270
+ archiveRoot: Fr.random(),
271
+ indexWithinCheckpoint: IndexWithinCheckpoint(1)
272
+ });
273
+ const proposal2 = await makeBlockProposal({
274
+ signer: signers[0],
275
+ blockHeader,
276
+ archiveRoot: Fr.random(),
277
+ indexWithinCheckpoint: IndexWithinCheckpoint(1)
278
+ });
279
+ await ap.tryAddBlockProposal(proposal1);
280
+ await ap.tryAddBlockProposal(proposal2);
281
+ const proposals = await ap.getProposalsForSlot(SlotNumber(slotNumber));
282
+ expect(proposals.blockProposals.map((proposal)=>proposal.toBuffer())).toEqual(expect.arrayContaining([
283
+ proposal1.withoutSignedTxs().toBuffer(),
284
+ proposal2.withoutSignedTxs().toBuffer()
285
+ ]));
286
+ expect(await ap.getBlockProposalByArchive(proposal2.archive.toString())).toBeDefined();
219
287
  });
220
288
  });
221
289
  describe('CheckpointProposal in attestation pool', ()=>{
222
- const mockCheckpointProposalForPool = (signer, slotNumber, archive = Fr.random())=>{
223
- const checkpointHeader = makeCheckpointHeader(1, {
290
+ const mockCheckpointProposalForPool = async (signer, slotNumber, archive = Fr.random(), checkpointHeader)=>{
291
+ const headerToUse = checkpointHeader ?? makeCheckpointHeader(1, {
224
292
  slotNumber: SlotNumber(slotNumber)
225
293
  });
226
294
  const blockHeader = makeBlockHeader(1);
227
- return makeCheckpointProposal({
295
+ const proposal = await makeCheckpointProposal({
228
296
  signer,
229
- checkpointHeader,
297
+ checkpointHeader: headerToUse,
230
298
  archiveRoot: archive,
231
299
  lastBlock: {
232
300
  blockHeader
233
301
  }
234
302
  });
303
+ // Return the core version since tryAddCheckpointProposal now takes CheckpointProposalCore
304
+ return proposal.toCore();
235
305
  };
236
- it('should add and retrieve checkpoint proposal as core (without lastBlock)', async ()=>{
306
+ it('should add and retrieve checkpoint proposal', async ()=>{
237
307
  const slotNumber = 420;
238
308
  const archive = Fr.random();
239
309
  const proposal = await mockCheckpointProposalForPool(signers[0], slotNumber, archive);
240
- const proposalId = proposal.archive.toString();
241
- await ap.addCheckpointProposal(proposal);
242
- const retrievedProposal = await ap.getCheckpointProposal(proposalId);
310
+ const result = await ap.tryAddCheckpointProposal(proposal);
311
+ expect(result.added).toBe(true);
312
+ expect(result.alreadyExists).toBe(false);
313
+ expect(result.count).toBe(1);
314
+ const retrievedProposal = await ap.getCheckpointProposal(SlotNumber(slotNumber));
243
315
  expect(retrievedProposal).toBeDefined();
244
- // Should return core version (without lastBlock)
245
- expect(retrievedProposal.toBuffer()).toEqual(proposal.toCore().toBuffer());
246
- // Check hasCheckpointProposal with both id and object
247
- expect(await ap.hasCheckpointProposal(proposalId)).toBe(true);
248
- expect(await ap.hasCheckpointProposal(proposal)).toBe(true);
316
+ expect(retrievedProposal.toBuffer()).toEqual(proposal.toBuffer());
249
317
  });
250
- it('should extract and store block proposal when adding checkpoint proposal with lastBlock', async ()=>{
251
- const slotNumber = 420;
252
- const archive = Fr.random();
253
- const proposal = await mockCheckpointProposalForPool(signers[0], slotNumber, archive);
254
- const proposalId = proposal.archive.toString();
255
- // Verify the proposal has a lastBlock
256
- const expectedBlockProposal = proposal.getBlockProposal();
257
- expect(expectedBlockProposal).toBeDefined();
258
- await ap.addCheckpointProposal(proposal);
259
- // The block proposal should be stored separately and retrievable
260
- const retrievedBlockProposal = await ap.getBlockProposal(proposalId);
261
- expect(retrievedBlockProposal).toBeDefined();
262
- expect(retrievedBlockProposal.archive.toString()).toBe(archive.toString());
263
- expect(retrievedBlockProposal.blockHeader.toBuffer()).toEqual(expectedBlockProposal.blockHeader.toBuffer());
264
- });
265
- it('should not store block proposal when checkpoint proposal has no lastBlock', async ()=>{
318
+ it('should handle checkpoint proposal without lastBlock (caller extracts and adds block separately)', async ()=>{
266
319
  const slotNumber = 420;
267
320
  const archive = Fr.random();
268
321
  const checkpointHeader = makeCheckpointHeader(1, {
@@ -274,47 +327,419 @@ export function describeAttestationPool(getAttestationPool) {
274
327
  checkpointHeader,
275
328
  archiveRoot: archive
276
329
  });
277
- const proposalId = proposal.archive.toString();
278
- await ap.addCheckpointProposal(proposal);
330
+ // Add the checkpoint core - block extraction is now caller responsibility
331
+ await ap.tryAddCheckpointProposal(proposal.toCore());
279
332
  // The checkpoint proposal should be stored
280
- const retrievedCheckpointProposal = await ap.getCheckpointProposal(proposalId);
333
+ const retrievedCheckpointProposal = await ap.getCheckpointProposal(SlotNumber(slotNumber));
281
334
  expect(retrievedCheckpointProposal).toBeDefined();
282
- // But no block proposal should be stored (archive key won't have a block proposal)
283
- const retrievedBlockProposal = await ap.getBlockProposal(proposalId);
335
+ // No block proposal was extracted (it had none anyway)
336
+ const retrievedBlockProposal = await ap.getBlockProposalByArchive(proposal.archive.toString());
284
337
  expect(retrievedBlockProposal).toBeUndefined();
285
338
  });
286
339
  it('should return undefined for non-existent checkpoint proposal', async ()=>{
287
- const nonExistentId = Fr.random().toString();
288
- const retrievedProposal = await ap.getCheckpointProposal(nonExistentId);
340
+ const retrievedProposal = await ap.getCheckpointProposal(SlotNumber(99999));
289
341
  expect(retrievedProposal).toBeUndefined();
290
- // Check hasCheckpointProposal returns false for non-existent proposal
291
- expect(await ap.hasCheckpointProposal(nonExistentId)).toBe(false);
292
342
  });
293
- it('should update checkpoint proposal if added twice with same id', async ()=>{
343
+ it('should return alreadyExists when re-adding the same signed payload', async ()=>{
294
344
  const slotNumber = 420;
295
345
  const archive = Fr.random();
296
- const proposal1 = await mockCheckpointProposalForPool(signers[0], slotNumber, archive);
297
- const proposalId = proposal1.archive.toString();
298
- await ap.addCheckpointProposal(proposal1);
299
- // Create a new proposal with same archive but different signer
300
- const proposal2 = await mockCheckpointProposalForPool(signers[1], slotNumber, archive);
301
- await ap.addCheckpointProposal(proposal2);
302
- const retrievedProposal = await ap.getCheckpointProposal(proposalId);
346
+ const proposal = await mockCheckpointProposalForPool(signers[0], slotNumber, archive);
347
+ const result1 = await ap.tryAddCheckpointProposal(proposal);
348
+ expect(result1.added).toBe(true);
349
+ expect(result1.alreadyExists).toBe(false);
350
+ // Re-broadcasting the exact same signed payload yields alreadyExists.
351
+ const result2 = await ap.tryAddCheckpointProposal(proposal);
352
+ expect(result2.added).toBe(false);
353
+ expect(result2.alreadyExists).toBe(true);
354
+ // Should still have the first proposal stored at the slot
355
+ const retrievedProposal = await ap.getCheckpointProposal(SlotNumber(slotNumber));
303
356
  expect(retrievedProposal).toBeDefined();
304
- // Should have the second proposal (as core)
305
- expect(retrievedProposal.toBuffer()).toEqual(proposal2.toCore().toBuffer());
306
- expect(retrievedProposal.getSender()?.toString()).toBe(signers[1].address.toString());
357
+ expect(retrievedProposal.toBuffer()).toEqual(proposal.toBuffer());
358
+ expect(retrievedProposal.getSender()?.toString()).toBe(signers[0].address.toString());
359
+ });
360
+ it('should treat distinct payloads at the same slot as equivocations (count = 2)', async ()=>{
361
+ const slotNumber = 420;
362
+ // Two proposals at the same slot but with different headers (distinct payloads).
363
+ const proposal1 = await mockCheckpointProposalForPool(signers[0], slotNumber, Fr.random());
364
+ const proposal2 = await mockCheckpointProposalForPool(signers[0], slotNumber, Fr.random());
365
+ const result1 = await ap.tryAddCheckpointProposal(proposal1);
366
+ expect(result1.added).toBe(true);
367
+ expect(result1.count).toBe(1);
368
+ const result2 = await ap.tryAddCheckpointProposal(proposal2);
369
+ // The second distinct payload is tracked as an equivocation, count goes to 2,
370
+ // and both accepted payloads are retained by payload hash.
371
+ expect(result2.added).toBe(true);
372
+ expect(result2.alreadyExists).toBe(false);
373
+ expect(result2.count).toBe(2);
374
+ const retrievedProposal = await ap.getCheckpointProposal(SlotNumber(slotNumber));
375
+ const expectedProposal = [
376
+ proposal1,
377
+ proposal2
378
+ ].sort((a, b)=>a.getPayloadHash().localeCompare(b.getPayloadHash()))[0];
379
+ expect(retrievedProposal.toBuffer()).toEqual(expectedProposal.toBuffer());
380
+ const proposals = await ap.getProposalsForSlot(SlotNumber(slotNumber));
381
+ expect(proposals.checkpointProposals.map((proposal)=>proposal.toBuffer())).toEqual(expect.arrayContaining([
382
+ proposal1.toBuffer(),
383
+ proposal2.toBuffer()
384
+ ]));
385
+ });
386
+ it('should detect equivocation when only feeAssetPriceModifier differs', async ()=>{
387
+ const slotNumber = 420;
388
+ const archive = Fr.random();
389
+ // Same checkpoint header + archive, but two different feeAssetPriceModifier values.
390
+ // This is the audit-finding scenario: archive collides but the signed payload differs.
391
+ const sharedHeader = makeCheckpointHeader(1, {
392
+ slotNumber: SlotNumber(slotNumber)
393
+ });
394
+ const proposalA = await makeCheckpointProposal({
395
+ signer: signers[0],
396
+ checkpointHeader: sharedHeader,
397
+ archiveRoot: archive,
398
+ feeAssetPriceModifier: 50n
399
+ });
400
+ const proposalB = await makeCheckpointProposal({
401
+ signer: signers[0],
402
+ checkpointHeader: sharedHeader,
403
+ archiveRoot: archive,
404
+ feeAssetPriceModifier: -50n
405
+ });
406
+ const result1 = await ap.tryAddCheckpointProposal(proposalA.toCore());
407
+ expect(result1.count).toBe(1);
408
+ const result2 = await ap.tryAddCheckpointProposal(proposalB.toCore());
409
+ // The fix: archive collision no longer hides the equivocation; payload-hash dedup
410
+ // sees the distinct feeMod and bumps `count` to 2 so libp2p can fire the slash callback.
411
+ expect(result2.added).toBe(true);
412
+ expect(result2.alreadyExists).toBe(false);
413
+ expect(result2.count).toBe(2);
414
+ });
415
+ it('should delete retained proposals older than a given slot', async ()=>{
416
+ const oldSlot = 100;
417
+ const newSlot = 200;
418
+ const oldBlock = await mockBlockProposalForPool(signers[0], oldSlot);
419
+ const newBlock = await mockBlockProposalForPool(signers[1], newSlot);
420
+ const oldCheckpoint = await mockCheckpointProposalForPool(signers[0], oldSlot);
421
+ const newCheckpoint = await mockCheckpointProposalForPool(signers[1], newSlot);
422
+ await ap.tryAddBlockProposal(oldBlock);
423
+ await ap.tryAddBlockProposal(newBlock);
424
+ await ap.tryAddCheckpointProposal(oldCheckpoint);
425
+ await ap.tryAddCheckpointProposal(newCheckpoint);
426
+ await ap.deleteOlderThan(SlotNumber(newSlot));
427
+ expect(await ap.getProposalsForSlot(SlotNumber(oldSlot))).toEqual({
428
+ blockProposals: [],
429
+ checkpointProposals: []
430
+ });
431
+ const newProposals = await ap.getProposalsForSlot(SlotNumber(newSlot));
432
+ expect(newProposals.blockProposals.map((proposal)=>proposal.toBuffer())).toContainEqual(newBlock.withoutSignedTxs().toBuffer());
433
+ expect(newProposals.checkpointProposals.map((proposal)=>proposal.toBuffer())).toContainEqual(newCheckpoint.toBuffer());
307
434
  });
308
- it('should throw ProposalSlotCapExceededError when exceeding capacity', async ()=>{
435
+ it('should return added=false when exceeding capacity', async ()=>{
309
436
  const slotNumber = 420;
310
- // Add MAX_PROPOSALS_PER_SLOT proposals
311
- for(let i = 0; i < MAX_PROPOSALS_PER_SLOT; i++){
437
+ // Add MAX_CHECKPOINT_PROPOSALS_PER_SLOT distinct proposals.
438
+ for(let i = 0; i < MAX_CHECKPOINT_PROPOSALS_PER_SLOT; i++){
312
439
  const proposal = await mockCheckpointProposalForPool(signers[i % NUMBER_OF_SIGNERS_PER_TEST], slotNumber);
313
- await ap.addCheckpointProposal(proposal);
440
+ const result = await ap.tryAddCheckpointProposal(proposal);
441
+ expect(result.added).toBe(true);
442
+ expect(result.count).toBe(i + 1);
314
443
  }
315
- // The next proposal should throw
444
+ // The next proposal should not be added.
316
445
  const extraProposal = await mockCheckpointProposalForPool(signers[0], slotNumber);
317
- await expect(ap.addCheckpointProposal(extraProposal)).rejects.toThrow('Maximum checkpoint proposals per slot');
446
+ const result = await ap.tryAddCheckpointProposal(extraProposal);
447
+ expect(result.added).toBe(false);
448
+ expect(result.alreadyExists).toBe(false);
449
+ expect(result.count).toBe(MAX_CHECKPOINT_PROPOSALS_PER_SLOT);
450
+ });
451
+ });
452
+ describe('Duplicate proposal detection', ()=>{
453
+ const mockBlockProposalWithIndex = (signer, slotNumber, indexWithinCheckpoint, archive = Fr.random())=>{
454
+ const header = makeBlockHeader(1, {
455
+ slotNumber: SlotNumber(slotNumber)
456
+ });
457
+ return makeBlockProposal({
458
+ signer,
459
+ blockHeader: header,
460
+ archiveRoot: archive,
461
+ indexWithinCheckpoint: IndexWithinCheckpoint(indexWithinCheckpoint)
462
+ });
463
+ };
464
+ describe('tryAddBlockProposal duplicate detection', ()=>{
465
+ it('should return count=1 when pool is empty', async ()=>{
466
+ const proposal = await mockBlockProposalWithIndex(signers[0], 100, 0);
467
+ const result = await ap.tryAddBlockProposal(proposal);
468
+ expect(result.added).toBe(true);
469
+ expect(result.alreadyExists).toBe(false);
470
+ expect(result.count).toBe(1);
471
+ });
472
+ it('should return alreadyExists when same proposal exists', async ()=>{
473
+ const proposal = await mockBlockProposalWithIndex(signers[0], 100, 0);
474
+ await ap.tryAddBlockProposal(proposal);
475
+ const result = await ap.tryAddBlockProposal(proposal);
476
+ expect(result.added).toBe(false);
477
+ expect(result.alreadyExists).toBe(true);
478
+ expect(result.count).toBe(1);
479
+ });
480
+ it('should detect duplicate via count when different proposal exists at same position', async ()=>{
481
+ const slotNumber = 100;
482
+ const indexWithinCheckpoint = 2;
483
+ // Add first proposal
484
+ const proposal1 = await mockBlockProposalWithIndex(signers[0], slotNumber, indexWithinCheckpoint);
485
+ const result1 = await ap.tryAddBlockProposal(proposal1);
486
+ expect(result1.count).toBe(1);
487
+ // Add a different proposal at same position - this is a duplicate (equivocation)
488
+ const proposal2 = await mockBlockProposalWithIndex(signers[1], slotNumber, indexWithinCheckpoint);
489
+ const result2 = await ap.tryAddBlockProposal(proposal2);
490
+ expect(result2.added).toBe(true);
491
+ expect(result2.alreadyExists).toBe(false);
492
+ // count >= 2 indicates duplicate detection
493
+ expect(result2.count).toBe(2);
494
+ });
495
+ it('should not detect duplicate for different positions in same slot', async ()=>{
496
+ const slotNumber = 100;
497
+ // Add proposal at index 0
498
+ const proposal1 = await mockBlockProposalWithIndex(signers[0], slotNumber, 0);
499
+ await ap.tryAddBlockProposal(proposal1);
500
+ // Add proposal at index 1 (different position)
501
+ const proposal2 = await mockBlockProposalWithIndex(signers[1], slotNumber, 1);
502
+ const result = await ap.tryAddBlockProposal(proposal2);
503
+ expect(result.added).toBe(true);
504
+ // count = 1 means no duplicate for this position
505
+ expect(result.count).toBe(1);
506
+ });
507
+ it('should not detect duplicate for same position in different slots', async ()=>{
508
+ const indexWithinCheckpoint = 0;
509
+ // Add proposal at slot 100
510
+ const proposal1 = await mockBlockProposalWithIndex(signers[0], 100, indexWithinCheckpoint);
511
+ await ap.tryAddBlockProposal(proposal1);
512
+ // Add proposal at slot 200 (different slot)
513
+ const proposal2 = await mockBlockProposalWithIndex(signers[1], 200, indexWithinCheckpoint);
514
+ const result = await ap.tryAddBlockProposal(proposal2);
515
+ expect(result.added).toBe(true);
516
+ // count = 1 means no duplicate for this position
517
+ expect(result.count).toBe(1);
518
+ });
519
+ it('should track multiple duplicates correctly via count', async ()=>{
520
+ const slotNumber = 100;
521
+ const indexWithinCheckpoint = 0;
522
+ // Add multiple proposals for same position
523
+ const proposal1 = await mockBlockProposalWithIndex(signers[0], slotNumber, indexWithinCheckpoint);
524
+ const result1 = await ap.tryAddBlockProposal(proposal1);
525
+ expect(result1.count).toBe(1);
526
+ const proposal2 = await mockBlockProposalWithIndex(signers[1], slotNumber, indexWithinCheckpoint);
527
+ const result2 = await ap.tryAddBlockProposal(proposal2);
528
+ expect(result2.count).toBe(2);
529
+ // Third proposal for same position should be rejected (cap is 2)
530
+ const proposal3 = await mockBlockProposalWithIndex(signers[2], slotNumber, indexWithinCheckpoint);
531
+ const result3 = await ap.tryAddBlockProposal(proposal3);
532
+ expect(result3.added).toBe(false);
533
+ expect(result3.count).toBe(2);
534
+ });
535
+ it('should return added=false when exceeding capacity', async ()=>{
536
+ const slotNumber = 100;
537
+ const indexWithinCheckpoint = 0;
538
+ // Add MAX_BLOCK_PROPOSALS_PER_POSITION proposals
539
+ for(let i = 0; i < MAX_BLOCK_PROPOSALS_PER_POSITION; i++){
540
+ const proposal = await mockBlockProposalWithIndex(signers[i % NUMBER_OF_SIGNERS_PER_TEST], slotNumber, indexWithinCheckpoint);
541
+ const result = await ap.tryAddBlockProposal(proposal);
542
+ expect(result.added).toBe(true);
543
+ expect(result.count).toBe(i + 1);
544
+ }
545
+ // The next proposal should not be added
546
+ const extraProposal = await mockBlockProposalWithIndex(signers[0], slotNumber, indexWithinCheckpoint);
547
+ const result = await ap.tryAddBlockProposal(extraProposal);
548
+ expect(result.added).toBe(false);
549
+ expect(result.alreadyExists).toBe(false);
550
+ expect(result.count).toBe(MAX_BLOCK_PROPOSALS_PER_POSITION);
551
+ });
552
+ it('should clean up block position index when deleting old data', async ()=>{
553
+ const slotNumber = 100;
554
+ const indexWithinCheckpoint = 0;
555
+ // Add proposal
556
+ const proposal1 = await mockBlockProposalWithIndex(signers[0], slotNumber, indexWithinCheckpoint);
557
+ await ap.tryAddBlockProposal(proposal1);
558
+ // Verify it's tracked (adding another should show count = 2)
559
+ const proposal2 = await mockBlockProposalWithIndex(signers[1], slotNumber, indexWithinCheckpoint);
560
+ let result = await ap.tryAddBlockProposal(proposal2);
561
+ expect(result.count).toBe(2);
562
+ // Delete old data
563
+ await ap.deleteOlderThan(SlotNumber(slotNumber + 1));
564
+ // Verify position index is cleaned up (count should be 1 now)
565
+ const proposal3 = await mockBlockProposalWithIndex(signers[2], slotNumber, indexWithinCheckpoint);
566
+ result = await ap.tryAddBlockProposal(proposal3);
567
+ expect(result.count).toBe(1);
568
+ });
569
+ it('should correctly delete block proposals at slot boundary', async ()=>{
570
+ // Add proposals at slots 99, 100, and 101 with various indices
571
+ const proposalSlot99Idx0 = await mockBlockProposalWithIndex(signers[0], 99, 0);
572
+ const proposalSlot99Idx1 = await mockBlockProposalWithIndex(signers[1], 99, 1);
573
+ const proposalSlot100Idx0 = await mockBlockProposalWithIndex(signers[2], 100, 0);
574
+ const proposalSlot101Idx0 = await mockBlockProposalWithIndex(signers[3], 101, 0);
575
+ await ap.tryAddBlockProposal(proposalSlot99Idx0);
576
+ await ap.tryAddBlockProposal(proposalSlot99Idx1);
577
+ await ap.tryAddBlockProposal(proposalSlot100Idx0);
578
+ await ap.tryAddBlockProposal(proposalSlot101Idx0);
579
+ // Delete slots older than 100 (should delete slot 99 only)
580
+ await ap.deleteOlderThan(SlotNumber(100));
581
+ // Slot 99 proposals should have their index cleaned up
582
+ const newProposal99 = await mockBlockProposalWithIndex(signers[0], 99, 0);
583
+ const result99 = await ap.tryAddBlockProposal(newProposal99);
584
+ expect(result99.count).toBe(1); // Index was cleaned up
585
+ // Slot 100 and 101 should still be tracked
586
+ const newProposal100 = await mockBlockProposalWithIndex(signers[1], 100, 0);
587
+ const result100 = await ap.tryAddBlockProposal(newProposal100);
588
+ expect(result100.count).toBe(2); // Still has the original
589
+ const newProposal101 = await mockBlockProposalWithIndex(signers[2], 101, 0);
590
+ const result101 = await ap.tryAddBlockProposal(newProposal101);
591
+ expect(result101.count).toBe(2); // Still has the original
592
+ });
593
+ it('should delete all indices for a given slot', async ()=>{
594
+ const slotNumber = 50;
595
+ // Add proposals at multiple indices for the same slot
596
+ const proposal0 = await mockBlockProposalWithIndex(signers[0], slotNumber, 0);
597
+ const proposal1 = await mockBlockProposalWithIndex(signers[1], slotNumber, 1);
598
+ const proposal2 = await mockBlockProposalWithIndex(signers[2], slotNumber, 2);
599
+ await ap.tryAddBlockProposal(proposal0);
600
+ await ap.tryAddBlockProposal(proposal1);
601
+ await ap.tryAddBlockProposal(proposal2);
602
+ // Delete slots older than slotNumber + 1
603
+ await ap.deleteOlderThan(SlotNumber(slotNumber + 1));
604
+ // All indices should be cleaned up
605
+ const newProposal0 = await mockBlockProposalWithIndex(signers[0], slotNumber, 0);
606
+ const result0 = await ap.tryAddBlockProposal(newProposal0);
607
+ expect(result0.count).toBe(1);
608
+ const newProposal1 = await mockBlockProposalWithIndex(signers[1], slotNumber, 1);
609
+ const result1 = await ap.tryAddBlockProposal(newProposal1);
610
+ expect(result1.count).toBe(1);
611
+ const newProposal2 = await mockBlockProposalWithIndex(signers[2], slotNumber, 2);
612
+ const result2 = await ap.tryAddBlockProposal(newProposal2);
613
+ expect(result2.count).toBe(1);
614
+ });
615
+ it('should delete block proposals from storage when deleting old data', async ()=>{
616
+ const oldSlot = 50;
617
+ const newSlot = 100;
618
+ // Add proposals at old and new slots
619
+ const oldProposal = await mockBlockProposalWithIndex(signers[0], oldSlot, 0);
620
+ const newProposal = await mockBlockProposalWithIndex(signers[1], newSlot, 0);
621
+ await ap.tryAddBlockProposal(oldProposal);
622
+ await ap.tryAddBlockProposal(newProposal);
623
+ // Verify both proposals exist
624
+ expect(await ap.getBlockProposalByArchive(oldProposal.archive.toString())).toBeDefined();
625
+ expect(await ap.getBlockProposalByArchive(newProposal.archive.toString())).toBeDefined();
626
+ // Delete slots older than newSlot (should delete oldSlot)
627
+ await ap.deleteOlderThan(SlotNumber(newSlot));
628
+ // Old proposal should be deleted from storage
629
+ expect(await ap.getBlockProposalByArchive(oldProposal.archive.toString())).toBeUndefined();
630
+ // New proposal should still exist
631
+ expect(await ap.getBlockProposalByArchive(newProposal.archive.toString())).toBeDefined();
632
+ });
633
+ });
634
+ describe('tryAddCheckpointProposal duplicate detection', ()=>{
635
+ const mockCheckpointProposalCoreForPool = async (signer, slotNumber, archive = Fr.random(), checkpointHeader)=>{
636
+ const headerToUse = checkpointHeader ?? makeCheckpointHeader(1, {
637
+ slotNumber: SlotNumber(slotNumber)
638
+ });
639
+ const blockHeader = makeBlockHeader(1);
640
+ const proposal = await makeCheckpointProposal({
641
+ signer,
642
+ checkpointHeader: headerToUse,
643
+ archiveRoot: archive,
644
+ lastBlock: {
645
+ blockHeader
646
+ }
647
+ });
648
+ return proposal.toCore();
649
+ };
650
+ it('should return count=1 when pool is empty', async ()=>{
651
+ const proposal = await mockCheckpointProposalCoreForPool(signers[0], 100);
652
+ const result = await ap.tryAddCheckpointProposal(proposal);
653
+ expect(result.added).toBe(true);
654
+ expect(result.alreadyExists).toBe(false);
655
+ expect(result.count).toBe(1);
656
+ });
657
+ it('should return alreadyExists when same proposal exists', async ()=>{
658
+ const proposal = await mockCheckpointProposalCoreForPool(signers[0], 100);
659
+ await ap.tryAddCheckpointProposal(proposal);
660
+ const result = await ap.tryAddCheckpointProposal(proposal);
661
+ expect(result.added).toBe(false);
662
+ expect(result.alreadyExists).toBe(true);
663
+ expect(result.count).toBe(1);
664
+ });
665
+ it('should detect duplicate via count when different proposal exists for same slot', async ()=>{
666
+ const slotNumber = 100;
667
+ // Add first proposal
668
+ const proposal1 = await mockCheckpointProposalCoreForPool(signers[0], slotNumber);
669
+ const result1 = await ap.tryAddCheckpointProposal(proposal1);
670
+ expect(result1.count).toBe(1);
671
+ // Add a different proposal for same slot - this is a duplicate (equivocation)
672
+ const proposal2 = await mockCheckpointProposalCoreForPool(signers[1], slotNumber);
673
+ const result2 = await ap.tryAddCheckpointProposal(proposal2);
674
+ expect(result2.added).toBe(true);
675
+ expect(result2.alreadyExists).toBe(false);
676
+ // count >= 2 indicates duplicate detection
677
+ expect(result2.count).toBe(2);
678
+ });
679
+ it('should not detect duplicate for different slots', async ()=>{
680
+ // Add proposal at slot 100
681
+ const proposal1 = await mockCheckpointProposalCoreForPool(signers[0], 100);
682
+ await ap.tryAddCheckpointProposal(proposal1);
683
+ // Add proposal at slot 200 (different slot)
684
+ const proposal2 = await mockCheckpointProposalCoreForPool(signers[1], 200);
685
+ const result = await ap.tryAddCheckpointProposal(proposal2);
686
+ expect(result.added).toBe(true);
687
+ // count = 1 means no duplicate for this slot
688
+ expect(result.count).toBe(1);
689
+ });
690
+ it('should track multiple duplicates correctly via count', async ()=>{
691
+ const slotNumber = 100;
692
+ // Add multiple proposals for same slot
693
+ const proposal1 = await mockCheckpointProposalCoreForPool(signers[0], slotNumber);
694
+ const result1 = await ap.tryAddCheckpointProposal(proposal1);
695
+ expect(result1.count).toBe(1);
696
+ const proposal2 = await mockCheckpointProposalCoreForPool(signers[1], slotNumber);
697
+ const result2 = await ap.tryAddCheckpointProposal(proposal2);
698
+ expect(result2.count).toBe(2);
699
+ // Third proposal for same slot should be rejected (cap is 2)
700
+ const proposal3 = await mockCheckpointProposalCoreForPool(signers[2], slotNumber);
701
+ const result3 = await ap.tryAddCheckpointProposal(proposal3);
702
+ expect(result3.added).toBe(false);
703
+ expect(result3.count).toBe(2);
704
+ });
705
+ it('should not count attestations as proposals for duplicate detection', async ()=>{
706
+ const slotNumber = 100;
707
+ const archive = Fr.random();
708
+ // Attestation arrives BEFORE the checkpoint proposal (race condition in p2p)
709
+ const attestation = mockCheckpointAttestation(signers[0], slotNumber, archive);
710
+ await ap.addOwnCheckpointAttestations([
711
+ attestation
712
+ ]);
713
+ // Now the checkpoint proposal arrives - this should NOT be detected as a duplicate
714
+ const proposal = await mockCheckpointProposalCoreForPool(signers[1], slotNumber, archive);
715
+ const result = await ap.tryAddCheckpointProposal(proposal);
716
+ expect(result.added).toBe(true);
717
+ expect(result.alreadyExists).toBe(false);
718
+ // count should be 1, NOT 2 - attestations should not count as proposals
719
+ expect(result.count).toBe(1);
720
+ });
721
+ it('should not count attestations for different proposals as duplicates', async ()=>{
722
+ const slotNumber = 100;
723
+ const archive1 = Fr.random();
724
+ const archive2 = Fr.random();
725
+ // Add attestations for two different proposals in the same slot
726
+ const attestation1 = mockCheckpointAttestation(signers[0], slotNumber, archive1);
727
+ const attestation2 = mockCheckpointAttestation(signers[1], slotNumber, archive2);
728
+ await ap.addOwnCheckpointAttestations([
729
+ attestation1,
730
+ attestation2
731
+ ]);
732
+ // Add the first checkpoint proposal - should not be affected by attestations
733
+ const proposal1 = await mockCheckpointProposalCoreForPool(signers[2], slotNumber, archive1);
734
+ const result1 = await ap.tryAddCheckpointProposal(proposal1);
735
+ expect(result1.added).toBe(true);
736
+ expect(result1.count).toBe(1);
737
+ // Add the second checkpoint proposal - this IS a duplicate (different archive, same slot)
738
+ const proposal2 = await mockCheckpointProposalCoreForPool(signers[3], slotNumber, archive2);
739
+ const result2 = await ap.tryAddCheckpointProposal(proposal2);
740
+ expect(result2.added).toBe(true);
741
+ expect(result2.count).toBe(2);
742
+ });
318
743
  });
319
744
  });
320
745
  }