@aztec/p2p 0.0.1-commit.aada20e3 → 0.0.1-commit.b1c78909e

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 (378) hide show
  1. package/dest/client/factory.d.ts +11 -11
  2. package/dest/client/factory.d.ts.map +1 -1
  3. package/dest/client/factory.js +45 -15
  4. package/dest/client/interface.d.ts +46 -33
  5. package/dest/client/interface.d.ts.map +1 -1
  6. package/dest/client/p2p_client.d.ts +39 -51
  7. package/dest/client/p2p_client.d.ts.map +1 -1
  8. package/dest/client/p2p_client.js +151 -224
  9. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +7 -8
  10. package/dest/config.d.ts +49 -18
  11. package/dest/config.d.ts.map +1 -1
  12. package/dest/config.js +92 -38
  13. package/dest/errors/tx-pool.error.d.ts +8 -0
  14. package/dest/errors/tx-pool.error.d.ts.map +1 -0
  15. package/dest/errors/tx-pool.error.js +9 -0
  16. package/dest/index.d.ts +2 -1
  17. package/dest/index.d.ts.map +1 -1
  18. package/dest/index.js +1 -0
  19. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +104 -88
  20. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  21. package/dest/mem_pools/attestation_pool/attestation_pool.js +445 -3
  22. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +2 -2
  23. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  24. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +353 -87
  25. package/dest/mem_pools/attestation_pool/index.d.ts +2 -3
  26. package/dest/mem_pools/attestation_pool/index.d.ts.map +1 -1
  27. package/dest/mem_pools/attestation_pool/index.js +1 -2
  28. package/dest/mem_pools/attestation_pool/mocks.d.ts +2 -2
  29. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  30. package/dest/mem_pools/attestation_pool/mocks.js +2 -2
  31. package/dest/mem_pools/index.d.ts +3 -2
  32. package/dest/mem_pools/index.d.ts.map +1 -1
  33. package/dest/mem_pools/index.js +1 -1
  34. package/dest/mem_pools/instrumentation.d.ts +4 -2
  35. package/dest/mem_pools/instrumentation.d.ts.map +1 -1
  36. package/dest/mem_pools/instrumentation.js +16 -14
  37. package/dest/mem_pools/interface.d.ts +5 -5
  38. package/dest/mem_pools/interface.d.ts.map +1 -1
  39. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.js +3 -3
  40. package/dest/mem_pools/tx_pool/priority.d.ts +2 -2
  41. package/dest/mem_pools/tx_pool/priority.d.ts.map +1 -1
  42. package/dest/mem_pools/tx_pool/priority.js +4 -3
  43. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +1 -1
  44. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +1 -1
  45. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +3 -1
  46. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +104 -0
  47. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -0
  48. package/dest/mem_pools/tx_pool_v2/deleted_pool.js +251 -0
  49. package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts +3 -3
  50. package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.d.ts.map +1 -1
  51. package/dest/mem_pools/tx_pool_v2/eviction/eviction_manager.js +18 -9
  52. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts +1 -1
  53. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -1
  54. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +5 -2
  55. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts +3 -3
  56. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts.map +1 -1
  57. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.js +12 -4
  58. package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts +2 -2
  59. package/dest/mem_pools/tx_pool_v2/eviction/index.d.ts.map +1 -1
  60. package/dest/mem_pools/tx_pool_v2/eviction/index.js +1 -1
  61. package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts +54 -5
  62. package/dest/mem_pools/tx_pool_v2/eviction/interfaces.d.ts.map +1 -1
  63. package/dest/mem_pools/tx_pool_v2/eviction/interfaces.js +8 -0
  64. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.js +7 -5
  65. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +7 -5
  66. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts +2 -2
  67. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts.map +1 -1
  68. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.js +14 -6
  69. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts +4 -4
  70. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.d.ts.map +1 -1
  71. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.js +16 -4
  72. package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts +3 -3
  73. package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.d.ts.map +1 -1
  74. package/dest/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.js +3 -3
  75. package/dest/mem_pools/tx_pool_v2/index.d.ts +3 -2
  76. package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -1
  77. package/dest/mem_pools/tx_pool_v2/index.js +2 -1
  78. package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts +15 -0
  79. package/dest/mem_pools/tx_pool_v2/instrumentation.d.ts.map +1 -0
  80. package/dest/mem_pools/tx_pool_v2/instrumentation.js +43 -0
  81. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +28 -8
  82. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
  83. package/dest/mem_pools/tx_pool_v2/interfaces.js +5 -1
  84. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +70 -14
  85. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
  86. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +132 -16
  87. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +108 -0
  88. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -0
  89. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +337 -0
  90. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +9 -4
  91. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
  92. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +20 -6
  93. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +14 -5
  94. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
  95. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +467 -592
  96. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +3 -3
  97. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
  98. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +6 -4
  99. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
  100. package/dest/msg_validators/proposal_validator/block_proposal_validator.js +10 -2
  101. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +6 -4
  102. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
  103. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +16 -2
  104. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +13 -8
  105. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
  106. package/dest/msg_validators/proposal_validator/proposal_validator.js +48 -36
  107. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +4 -4
  108. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
  109. package/dest/msg_validators/tx_validator/aggregate_tx_validator.js +3 -3
  110. package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts +2 -1
  111. package/dest/msg_validators/tx_validator/allowed_public_setup.d.ts.map +1 -1
  112. package/dest/msg_validators/tx_validator/allowed_public_setup.js +24 -20
  113. package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts +17 -0
  114. package/dest/msg_validators/tx_validator/allowed_setup_helpers.d.ts.map +1 -0
  115. package/dest/msg_validators/tx_validator/allowed_setup_helpers.js +24 -0
  116. package/dest/msg_validators/tx_validator/block_header_validator.d.ts +16 -3
  117. package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
  118. package/dest/msg_validators/tx_validator/block_header_validator.js +1 -1
  119. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +13 -3
  120. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
  121. package/dest/msg_validators/tx_validator/double_spend_validator.js +4 -4
  122. package/dest/msg_validators/tx_validator/factory.d.ts +125 -6
  123. package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
  124. package/dest/msg_validators/tx_validator/factory.js +226 -58
  125. package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts +1 -1
  126. package/dest/msg_validators/tx_validator/fee_payer_balance.d.ts.map +1 -1
  127. package/dest/msg_validators/tx_validator/fee_payer_balance.js +6 -2
  128. package/dest/msg_validators/tx_validator/gas_validator.d.ts +67 -3
  129. package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
  130. package/dest/msg_validators/tx_validator/gas_validator.js +104 -37
  131. package/dest/msg_validators/tx_validator/index.d.ts +3 -1
  132. package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
  133. package/dest/msg_validators/tx_validator/index.js +2 -0
  134. package/dest/msg_validators/tx_validator/metadata_validator.d.ts +1 -1
  135. package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
  136. package/dest/msg_validators/tx_validator/metadata_validator.js +4 -4
  137. package/dest/msg_validators/tx_validator/nullifier_cache.d.ts +14 -0
  138. package/dest/msg_validators/tx_validator/nullifier_cache.d.ts.map +1 -0
  139. package/dest/msg_validators/tx_validator/nullifier_cache.js +24 -0
  140. package/dest/msg_validators/tx_validator/phases_validator.d.ts +2 -2
  141. package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
  142. package/dest/msg_validators/tx_validator/phases_validator.js +44 -23
  143. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +20 -4
  144. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -1
  145. package/dest/msg_validators/tx_validator/timestamp_validator.js +6 -6
  146. package/dest/services/dummy_service.d.ts +13 -5
  147. package/dest/services/dummy_service.d.ts.map +1 -1
  148. package/dest/services/dummy_service.js +10 -4
  149. package/dest/services/encoding.d.ts +3 -3
  150. package/dest/services/encoding.d.ts.map +1 -1
  151. package/dest/services/encoding.js +11 -10
  152. package/dest/services/gossipsub/index.d.ts +3 -0
  153. package/dest/services/gossipsub/index.d.ts.map +1 -0
  154. package/dest/services/gossipsub/index.js +2 -0
  155. package/dest/services/gossipsub/scoring.d.ts +21 -3
  156. package/dest/services/gossipsub/scoring.d.ts.map +1 -1
  157. package/dest/services/gossipsub/scoring.js +24 -7
  158. package/dest/services/gossipsub/topic_score_params.d.ts +173 -0
  159. package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -0
  160. package/dest/services/gossipsub/topic_score_params.js +346 -0
  161. package/dest/services/libp2p/libp2p_service.d.ts +94 -42
  162. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  163. package/dest/services/libp2p/libp2p_service.js +463 -350
  164. package/dest/services/peer-manager/metrics.d.ts +3 -1
  165. package/dest/services/peer-manager/metrics.d.ts.map +1 -1
  166. package/dest/services/peer-manager/metrics.js +6 -0
  167. package/dest/services/peer-manager/peer_manager.d.ts +1 -1
  168. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  169. package/dest/services/peer-manager/peer_manager.js +2 -1
  170. package/dest/services/peer-manager/peer_scoring.d.ts +1 -1
  171. package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
  172. package/dest/services/peer-manager/peer_scoring.js +25 -2
  173. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +6 -5
  174. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
  175. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +26 -53
  176. package/dest/services/reqresp/batch-tx-requester/interface.d.ts +2 -6
  177. package/dest/services/reqresp/batch-tx-requester/interface.d.ts.map +1 -1
  178. package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts +10 -13
  179. package/dest/services/reqresp/batch-tx-requester/missing_txs.d.ts.map +1 -1
  180. package/dest/services/reqresp/batch-tx-requester/missing_txs.js +25 -46
  181. package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts +17 -11
  182. package/dest/services/reqresp/batch-tx-requester/peer_collection.d.ts.map +1 -1
  183. package/dest/services/reqresp/batch-tx-requester/peer_collection.js +49 -15
  184. package/dest/services/reqresp/batch-tx-requester/tx_validator.js +2 -2
  185. package/dest/services/reqresp/interface.d.ts +10 -1
  186. package/dest/services/reqresp/interface.d.ts.map +1 -1
  187. package/dest/services/reqresp/interface.js +15 -1
  188. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +7 -5
  189. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
  190. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +16 -11
  191. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +21 -10
  192. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
  193. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +27 -11
  194. package/dest/services/reqresp/protocols/tx.d.ts +7 -1
  195. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
  196. package/dest/services/reqresp/protocols/tx.js +20 -0
  197. package/dest/services/reqresp/reqresp.d.ts +1 -1
  198. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  199. package/dest/services/reqresp/reqresp.js +13 -5
  200. package/dest/services/service.d.ts +39 -3
  201. package/dest/services/service.d.ts.map +1 -1
  202. package/dest/services/tx_collection/config.d.ts +22 -4
  203. package/dest/services/tx_collection/config.d.ts.map +1 -1
  204. package/dest/services/tx_collection/config.js +49 -3
  205. package/dest/services/tx_collection/fast_tx_collection.d.ts +6 -5
  206. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
  207. package/dest/services/tx_collection/fast_tx_collection.js +64 -48
  208. package/dest/services/tx_collection/file_store_tx_collection.d.ts +53 -0
  209. package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -0
  210. package/dest/services/tx_collection/file_store_tx_collection.js +167 -0
  211. package/dest/services/tx_collection/file_store_tx_source.d.ts +37 -0
  212. package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -0
  213. package/dest/services/tx_collection/file_store_tx_source.js +90 -0
  214. package/dest/services/tx_collection/index.d.ts +3 -2
  215. package/dest/services/tx_collection/index.d.ts.map +1 -1
  216. package/dest/services/tx_collection/index.js +1 -0
  217. package/dest/services/tx_collection/instrumentation.d.ts +1 -1
  218. package/dest/services/tx_collection/instrumentation.d.ts.map +1 -1
  219. package/dest/services/tx_collection/instrumentation.js +2 -1
  220. package/dest/services/tx_collection/missing_txs_tracker.d.ts +32 -0
  221. package/dest/services/tx_collection/missing_txs_tracker.d.ts.map +1 -0
  222. package/dest/services/tx_collection/missing_txs_tracker.js +27 -0
  223. package/dest/services/tx_collection/proposal_tx_collector.d.ts +15 -14
  224. package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +1 -1
  225. package/dest/services/tx_collection/proposal_tx_collector.js +6 -6
  226. package/dest/services/tx_collection/slow_tx_collection.d.ts +7 -3
  227. package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -1
  228. package/dest/services/tx_collection/slow_tx_collection.js +60 -26
  229. package/dest/services/tx_collection/tx_collection.d.ts +23 -10
  230. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
  231. package/dest/services/tx_collection/tx_collection.js +75 -3
  232. package/dest/services/tx_collection/tx_collection_sink.d.ts +18 -8
  233. package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
  234. package/dest/services/tx_collection/tx_collection_sink.js +26 -29
  235. package/dest/services/tx_collection/tx_source.d.ts +8 -3
  236. package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
  237. package/dest/services/tx_collection/tx_source.js +19 -2
  238. package/dest/services/tx_file_store/config.d.ts +1 -3
  239. package/dest/services/tx_file_store/config.d.ts.map +1 -1
  240. package/dest/services/tx_file_store/config.js +0 -4
  241. package/dest/services/tx_file_store/tx_file_store.d.ts +4 -3
  242. package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -1
  243. package/dest/services/tx_file_store/tx_file_store.js +9 -6
  244. package/dest/services/tx_provider.d.ts +4 -4
  245. package/dest/services/tx_provider.d.ts.map +1 -1
  246. package/dest/services/tx_provider.js +9 -8
  247. package/dest/test-helpers/make-test-p2p-clients.d.ts +7 -8
  248. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  249. package/dest/test-helpers/make-test-p2p-clients.js +1 -2
  250. package/dest/test-helpers/mock-pubsub.d.ts +30 -4
  251. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  252. package/dest/test-helpers/mock-pubsub.js +105 -4
  253. package/dest/test-helpers/reqresp-nodes.d.ts +2 -3
  254. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  255. package/dest/test-helpers/reqresp-nodes.js +4 -3
  256. package/dest/test-helpers/testbench-utils.d.ts +43 -38
  257. package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
  258. package/dest/test-helpers/testbench-utils.js +129 -59
  259. package/dest/testbench/p2p_client_testbench_worker.d.ts +2 -2
  260. package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -1
  261. package/dest/testbench/p2p_client_testbench_worker.js +17 -16
  262. package/dest/testbench/worker_client_manager.d.ts +3 -1
  263. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  264. package/dest/testbench/worker_client_manager.js +6 -2
  265. package/dest/util.d.ts +3 -3
  266. package/dest/util.d.ts.map +1 -1
  267. package/package.json +14 -14
  268. package/src/client/factory.ts +82 -28
  269. package/src/client/interface.ts +56 -34
  270. package/src/client/p2p_client.ts +181 -269
  271. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +21 -12
  272. package/src/config.ts +141 -44
  273. package/src/errors/tx-pool.error.ts +12 -0
  274. package/src/index.ts +1 -0
  275. package/src/mem_pools/attestation_pool/attestation_pool.ts +497 -91
  276. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +442 -102
  277. package/src/mem_pools/attestation_pool/index.ts +9 -2
  278. package/src/mem_pools/attestation_pool/mocks.ts +2 -1
  279. package/src/mem_pools/index.ts +4 -1
  280. package/src/mem_pools/instrumentation.ts +17 -13
  281. package/src/mem_pools/interface.ts +4 -4
  282. package/src/mem_pools/tx_pool/README.md +1 -1
  283. package/src/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.ts +3 -3
  284. package/src/mem_pools/tx_pool/priority.ts +6 -3
  285. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +3 -1
  286. package/src/mem_pools/tx_pool_v2/README.md +112 -17
  287. package/src/mem_pools/tx_pool_v2/deleted_pool.ts +321 -0
  288. package/src/mem_pools/tx_pool_v2/eviction/eviction_manager.ts +21 -8
  289. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +5 -2
  290. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +18 -4
  291. package/src/mem_pools/tx_pool_v2/eviction/index.ts +4 -0
  292. package/src/mem_pools/tx_pool_v2/eviction/interfaces.ts +59 -4
  293. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.ts +5 -5
  294. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +5 -5
  295. package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +14 -9
  296. package/src/mem_pools/tx_pool_v2/eviction/low_priority_pre_add_rule.ts +33 -6
  297. package/src/mem_pools/tx_pool_v2/eviction/nullifier_conflict_rule.ts +4 -3
  298. package/src/mem_pools/tx_pool_v2/index.ts +2 -1
  299. package/src/mem_pools/tx_pool_v2/instrumentation.ts +69 -0
  300. package/src/mem_pools/tx_pool_v2/interfaces.ts +28 -8
  301. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +191 -24
  302. package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +430 -0
  303. package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +24 -7
  304. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +508 -676
  305. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +2 -2
  306. package/src/msg_validators/proposal_validator/block_proposal_validator.ts +14 -4
  307. package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +20 -7
  308. package/src/msg_validators/proposal_validator/proposal_validator.ts +63 -40
  309. package/src/msg_validators/tx_validator/README.md +115 -0
  310. package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +5 -5
  311. package/src/msg_validators/tx_validator/allowed_public_setup.ts +22 -27
  312. package/src/msg_validators/tx_validator/allowed_setup_helpers.ts +31 -0
  313. package/src/msg_validators/tx_validator/block_header_validator.ts +15 -3
  314. package/src/msg_validators/tx_validator/double_spend_validator.ts +11 -6
  315. package/src/msg_validators/tx_validator/factory.ts +366 -77
  316. package/src/msg_validators/tx_validator/fee_payer_balance.ts +6 -2
  317. package/src/msg_validators/tx_validator/gas_validator.ts +123 -27
  318. package/src/msg_validators/tx_validator/index.ts +2 -0
  319. package/src/msg_validators/tx_validator/metadata_validator.ts +12 -4
  320. package/src/msg_validators/tx_validator/nullifier_cache.ts +30 -0
  321. package/src/msg_validators/tx_validator/phases_validator.ts +51 -26
  322. package/src/msg_validators/tx_validator/timestamp_validator.ts +23 -18
  323. package/src/services/dummy_service.ts +18 -6
  324. package/src/services/encoding.ts +9 -9
  325. package/src/services/gossipsub/README.md +641 -0
  326. package/src/services/gossipsub/index.ts +2 -0
  327. package/src/services/gossipsub/scoring.ts +29 -5
  328. package/src/services/gossipsub/topic_score_params.ts +487 -0
  329. package/src/services/libp2p/libp2p_service.ts +488 -372
  330. package/src/services/peer-manager/metrics.ts +7 -0
  331. package/src/services/peer-manager/peer_manager.ts +2 -1
  332. package/src/services/peer-manager/peer_scoring.ts +25 -0
  333. package/src/services/reqresp/batch-tx-requester/README.md +7 -7
  334. package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +31 -59
  335. package/src/services/reqresp/batch-tx-requester/interface.ts +1 -5
  336. package/src/services/reqresp/batch-tx-requester/missing_txs.ts +23 -71
  337. package/src/services/reqresp/batch-tx-requester/peer_collection.ts +63 -24
  338. package/src/services/reqresp/batch-tx-requester/tx_validator.ts +2 -2
  339. package/src/services/reqresp/interface.ts +26 -1
  340. package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +23 -14
  341. package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +38 -15
  342. package/src/services/reqresp/protocols/tx.ts +22 -0
  343. package/src/services/reqresp/reqresp.ts +16 -4
  344. package/src/services/service.ts +51 -2
  345. package/src/services/tx_collection/config.ts +74 -6
  346. package/src/services/tx_collection/fast_tx_collection.ts +74 -51
  347. package/src/services/tx_collection/file_store_tx_collection.ts +202 -0
  348. package/src/services/tx_collection/file_store_tx_source.ts +117 -0
  349. package/src/services/tx_collection/index.ts +2 -1
  350. package/src/services/tx_collection/instrumentation.ts +7 -1
  351. package/src/services/tx_collection/missing_txs_tracker.ts +52 -0
  352. package/src/services/tx_collection/proposal_tx_collector.ts +20 -21
  353. package/src/services/tx_collection/slow_tx_collection.ts +66 -33
  354. package/src/services/tx_collection/tx_collection.ts +113 -16
  355. package/src/services/tx_collection/tx_collection_sink.ts +30 -34
  356. package/src/services/tx_collection/tx_source.ts +22 -3
  357. package/src/services/tx_file_store/config.ts +0 -6
  358. package/src/services/tx_file_store/tx_file_store.ts +10 -8
  359. package/src/services/tx_provider.ts +10 -9
  360. package/src/test-helpers/make-test-p2p-clients.ts +4 -6
  361. package/src/test-helpers/mock-pubsub.ts +146 -9
  362. package/src/test-helpers/reqresp-nodes.ts +5 -7
  363. package/src/test-helpers/testbench-utils.ts +128 -71
  364. package/src/testbench/p2p_client_testbench_worker.ts +26 -22
  365. package/src/testbench/worker_client_manager.ts +13 -5
  366. package/src/util.ts +8 -2
  367. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +0 -40
  368. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +0 -1
  369. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +0 -218
  370. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +0 -31
  371. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +0 -1
  372. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +0 -180
  373. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts +0 -23
  374. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts.map +0 -1
  375. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.js +0 -212
  376. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +0 -320
  377. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +0 -264
  378. package/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts +0 -230
@@ -1,5 +1,6 @@
1
- import { SlotNumber } from '@aztec/foundation/branded-types';
1
+ import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
2
2
  import type { Logger } from '@aztec/foundation/log';
3
+ import type { DateProvider } from '@aztec/foundation/timer';
3
4
  import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
4
5
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
5
6
  import { computeFeePayerBalanceStorageSlot } from '@aztec/protocol-contracts/fee-juice';
@@ -8,8 +9,10 @@ import type { L2Block, L2BlockId, L2BlockSource } from '@aztec/stdlib/block';
8
9
  import type { WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
9
10
  import { DatabasePublicStateSource } from '@aztec/stdlib/trees';
10
11
  import { BlockHeader, Tx, TxHash, type TxValidator } from '@aztec/stdlib/tx';
12
+ import type { TelemetryClient } from '@aztec/telemetry-client';
11
13
 
12
14
  import { TxArchive } from './archive/index.js';
15
+ import { DeletedPool } from './deleted_pool.js';
13
16
  import {
14
17
  EvictionManager,
15
18
  FeePayerBalanceEvictionRule,
@@ -20,8 +23,12 @@ import {
20
23
  LowPriorityPreAddRule,
21
24
  NullifierConflictRule,
22
25
  type PoolOperations,
26
+ type PreAddContext,
23
27
  type PreAddPoolAccess,
28
+ TxPoolRejectionCode,
29
+ type TxPoolRejectionError,
24
30
  } from './eviction/index.js';
31
+ import { TxPoolV2Instrumentation } from './instrumentation.js';
25
32
  import {
26
33
  type AddTxsResult,
27
34
  DEFAULT_TX_POOL_V2_CONFIG,
@@ -29,14 +36,8 @@ import {
29
36
  type TxPoolV2Config,
30
37
  type TxPoolV2Dependencies,
31
38
  } from './interfaces.js';
32
- import {
33
- type TxMetaData,
34
- type TxState,
35
- buildTxMetaData,
36
- checkNullifierConflict,
37
- compareFee,
38
- compareTxHash,
39
- } from './tx_metadata.js';
39
+ import { type TxMetaData, type TxState, buildTxMetaData, checkNullifierConflict } from './tx_metadata.js';
40
+ import { TxPoolIndices } from './tx_pool_indices.js';
40
41
 
41
42
  /**
42
43
  * Callbacks for the implementation to notify the outer class about events and metrics.
@@ -44,6 +45,7 @@ import {
44
45
  export interface TxPoolV2Callbacks {
45
46
  onTxsAdded: (txs: Tx[], opts: { source?: string }) => void;
46
47
  onTxsRemoved: (txHashes: string[] | bigint[]) => void;
48
+ onTxsMined: (txHashes: string[]) => void;
47
49
  }
48
50
 
49
51
  /**
@@ -59,27 +61,19 @@ export class TxPoolV2Impl {
59
61
  // === Dependencies ===
60
62
  #l2BlockSource: L2BlockSource;
61
63
  #worldStateSynchronizer: WorldStateSynchronizer;
62
- #pendingTxValidator: TxValidator<Tx>;
64
+ #createTxValidator: TxPoolV2Dependencies['createTxValidator'];
63
65
 
64
66
  // === In-Memory Indices ===
65
- /** Primary metadata store: txHash -> TxMetaData */
66
- #metadata: Map<string, TxMetaData> = new Map();
67
- /** Nullifier to txHash index (pending txs only) */
68
- #nullifierToTxHash: Map<string, string> = new Map();
69
- /** Fee payer to txHashes index (pending txs only) */
70
- #feePayerToTxHashes: Map<string, Set<string>> = new Map();
71
- /**
72
- * Pending txHashes grouped by priority fee.
73
- * Outer map: priorityFee -> Set of txHashes at that fee level.
74
- */
75
- #pendingByPriority: Map<bigint, Set<string>> = new Map();
76
- /** Protected transactions: txHash -> slotNumber. Includes txs we have and txs we expect to receive. */
77
- #protectedTransactions: Map<string, SlotNumber> = new Map();
67
+ #indices: TxPoolIndices = new TxPoolIndices();
78
68
 
79
69
  // === Config & Services ===
80
70
  #config: TxPoolV2Config;
81
71
  #archive: TxArchive;
72
+ #deletedPool: DeletedPool;
82
73
  #evictionManager: EvictionManager;
74
+ #dateProvider: DateProvider;
75
+ #instrumentation: TxPoolV2Instrumentation;
76
+ #evictedTxHashes: Set<string> = new Set();
83
77
  #log: Logger;
84
78
  #callbacks: TxPoolV2Callbacks;
85
79
 
@@ -88,7 +82,9 @@ export class TxPoolV2Impl {
88
82
  archiveStore: AztecAsyncKVStore,
89
83
  deps: TxPoolV2Dependencies,
90
84
  callbacks: TxPoolV2Callbacks,
85
+ telemetry: TelemetryClient,
91
86
  config: Partial<TxPoolV2Config> = {},
87
+ dateProvider: DateProvider,
92
88
  log: Logger,
93
89
  ) {
94
90
  this.#store = store;
@@ -96,10 +92,13 @@ export class TxPoolV2Impl {
96
92
 
97
93
  this.#l2BlockSource = deps.l2BlockSource;
98
94
  this.#worldStateSynchronizer = deps.worldStateSynchronizer;
99
- this.#pendingTxValidator = deps.pendingTxValidator;
95
+ this.#createTxValidator = deps.createTxValidator;
100
96
 
101
97
  this.#config = { ...DEFAULT_TX_POOL_V2_CONFIG, ...config };
102
98
  this.#archive = new TxArchive(archiveStore, this.#config.archivedTxLimit, log);
99
+ this.#deletedPool = new DeletedPool(store, this.#txsDB, log);
100
+ this.#dateProvider = dateProvider;
101
+ this.#instrumentation = new TxPoolV2Instrumentation(telemetry, () => this.#indices.getTotalMetadataBytes());
103
102
  this.#log = log;
104
103
  this.#callbacks = callbacks;
105
104
 
@@ -134,26 +133,42 @@ export class TxPoolV2Impl {
134
133
  * by running pre-add rules to resolve nullifier conflicts, balance checks, and pool size limits.
135
134
  */
136
135
  async hydrateFromDatabase(): Promise<void> {
137
- // Step 1: Load all transactions from DB
136
+ // Step 0: Hydrate deleted pool state
137
+ await this.#deletedPool.hydrateFromDatabase();
138
+
139
+ // Step 1: Load all transactions from DB (excluding soft-deleted)
138
140
  const { loaded, errors: deserializationErrors } = await this.#loadAllTxsFromDb();
139
141
 
140
142
  // Step 2: Check mined status for each tx
141
143
  await this.#markMinedStatusBatch(loaded.map(l => l.meta));
142
144
 
143
145
  // Step 3: Partition by mined status
144
- const { mined, nonMined } = this.#partitionByMinedStatus(loaded);
146
+ const mined: TxMetaData[] = [];
147
+ const nonMined: { tx: Tx; meta: TxMetaData }[] = [];
148
+ for (const entry of loaded) {
149
+ if (entry.meta.minedL2BlockId !== undefined) {
150
+ mined.push(entry.meta);
151
+ } else {
152
+ nonMined.push(entry);
153
+ }
154
+ }
145
155
 
146
156
  // Step 4: Validate non-mined transactions
147
- const { valid, invalid } = await this.#validateNonMinedTxs(nonMined);
157
+ const { valid, invalid } = await this.#revalidateMetadata(
158
+ nonMined.map(e => e.meta),
159
+ 'on startup',
160
+ );
148
161
 
149
162
  // Step 5: Populate mined indices (these don't need conflict resolution)
150
- this.#populateMinedIndices(mined);
163
+ for (const meta of mined) {
164
+ this.#indices.addMined(meta);
165
+ }
151
166
 
152
167
  // Step 6: Rebuild pending pool by running pre-add rules for each tx
153
168
  // This resolves nullifier conflicts, fee payer balance issues, and pool size limits
154
169
  const { rejected } = await this.#rebuildPendingPool(valid);
155
170
 
156
- // Step 7: Delete invalid and rejected txs from DB
171
+ // Step 7: Delete invalid and rejected txs from DB only (indices were never populated for these)
157
172
  const toDelete = [...deserializationErrors, ...invalid, ...rejected];
158
173
  if (toDelete.length === 0) {
159
174
  return;
@@ -163,17 +178,45 @@ export class TxPoolV2Impl {
163
178
  await this.#txsDB.delete(txHashStr);
164
179
  }
165
180
  });
166
- this.#log.info(`Deleted ${toDelete.length} invalid/rejected transactions on startup`);
181
+ this.#log.info(`Deleted ${toDelete.length} invalid/rejected transactions on startup`, { txHashes: toDelete });
167
182
  }
168
183
 
169
- async addPendingTxs(txs: Tx[], opts: { source?: string }): Promise<AddTxsResult> {
184
+ async addPendingTxs(txs: Tx[], opts: { source?: string; feeComparisonOnly?: boolean }): Promise<AddTxsResult> {
170
185
  const accepted: TxHash[] = [];
171
186
  const ignored: TxHash[] = [];
172
187
  const rejected: TxHash[] = [];
173
- const newlyAdded: Tx[] = [];
188
+ const errors = new Map<string, TxPoolRejectionError>();
174
189
  const acceptedPending = new Set<string>();
175
190
 
191
+ // Phase 1: Pre-compute all throwable I/O outside the transaction.
192
+ // If any pre-computation throws, the entire call fails before mutations happen.
193
+ const precomputed = new Map<string, { meta: TxMetaData; minedBlockId: L2BlockId | undefined; isValid: boolean }>();
194
+
195
+ const validator = await this.#createTxValidator();
196
+
197
+ for (const tx of txs) {
198
+ const txHash = tx.getTxHash();
199
+ const txHashStr = txHash.toString();
200
+
201
+ const meta = await buildTxMetaData(tx);
202
+ const minedBlockId = await this.#getMinedBlockId(txHash);
203
+
204
+ // Validate non-mined txs (mined and pre-protected txs bypass validation inside the transaction)
205
+ let isValid = true;
206
+ if (!minedBlockId) {
207
+ isValid = await this.#validateMeta(meta, validator);
208
+ }
209
+
210
+ precomputed.set(txHashStr, { meta, minedBlockId, isValid });
211
+ }
212
+
213
+ // Phase 2: Apply mutations inside the transaction using only pre-computed results,
214
+ // in-memory reads, and buffered DB writes. Nothing here can throw an unhandled exception.
176
215
  const poolAccess = this.#createPreAddPoolAccess();
216
+ const preAddContext: PreAddContext | undefined =
217
+ opts.feeComparisonOnly !== undefined
218
+ ? { feeComparisonOnly: opts.feeComparisonOnly, priceBumpPercentage: this.#config.priceBumpPercentage }
219
+ : undefined;
177
220
 
178
221
  await this.#store.transactionAsync(async () => {
179
222
  for (const tx of txs) {
@@ -181,38 +224,51 @@ export class TxPoolV2Impl {
181
224
  const txHashStr = txHash.toString();
182
225
 
183
226
  // Skip duplicates
184
- if (this.#isDuplicateTx(txHashStr)) {
227
+ if (this.#indices.has(txHashStr)) {
185
228
  ignored.push(txHash);
186
229
  continue;
187
230
  }
188
231
 
189
- // Check mined status first (applies to all paths)
190
- const minedBlockId = await this.#getMinedBlockId(txHash);
191
- const preProtectedSlot = this.#protectedTransactions.get(txHashStr);
232
+ const { meta, minedBlockId, isValid } = precomputed.get(txHashStr)!;
233
+ const preProtectedSlot = this.#indices.getProtectionSlot(txHashStr);
192
234
 
193
235
  if (minedBlockId) {
194
236
  // Already mined - add directly (protection already set if pre-protected)
195
- await this.#addNewMinedTx(tx, minedBlockId);
237
+ await this.#addTx(tx, { mined: minedBlockId }, opts, meta);
196
238
  accepted.push(txHash);
197
- newlyAdded.push(tx);
198
239
  } else if (preProtectedSlot !== undefined) {
199
240
  // Pre-protected and not mined - add as protected (bypass validation)
200
- await this.#addNewProtectedTx(tx, preProtectedSlot);
241
+ await this.#addTx(tx, { protected: preProtectedSlot }, opts, meta);
201
242
  accepted.push(txHash);
202
- newlyAdded.push(tx);
243
+ } else if (!isValid) {
244
+ // Failed pre-computed validation
245
+ rejected.push(txHash);
203
246
  } else {
204
- // Regular pending tx - validate and run pre-add rules
205
- const result = await this.#tryAddRegularPendingTx(tx, poolAccess, acceptedPending, ignored);
247
+ // Regular pending tx - run pre-add rules using pre-computed metadata
248
+ const result = await this.#tryAddRegularPendingTx(
249
+ tx,
250
+ meta,
251
+ opts,
252
+ poolAccess,
253
+ acceptedPending,
254
+ ignored,
255
+ errors,
256
+ preAddContext,
257
+ );
206
258
  if (result.status === 'accepted') {
207
259
  acceptedPending.add(txHashStr);
208
- newlyAdded.push(tx);
209
- } else if (result.status === 'rejected') {
210
- rejected.push(txHash);
211
260
  } else {
212
261
  ignored.push(txHash);
213
262
  }
214
263
  }
215
264
  }
265
+
266
+ // Run post-add eviction rules for pending txs (inside transaction for atomicity)
267
+ if (acceptedPending.size > 0) {
268
+ const feePayers = Array.from(acceptedPending).map(txHash => this.#indices.getMetadata(txHash)!.feePayer);
269
+ const uniqueFeePayers = new Set<string>(feePayers);
270
+ await this.#evictionManager.evictAfterNewTxs(Array.from(acceptedPending), [...uniqueFeePayers]);
271
+ }
216
272
  });
217
273
 
218
274
  // Build final accepted list for pending txs (excludes intra-batch evictions)
@@ -220,135 +276,183 @@ export class TxPoolV2Impl {
220
276
  accepted.push(TxHash.fromString(txHashStr));
221
277
  }
222
278
 
223
- // Run post-add eviction rules for pending txs
224
- if (acceptedPending.size > 0) {
225
- const feePayers = Array.from(acceptedPending).map(txHash => this.#metadata.get(txHash)!.feePayer);
226
- const uniqueFeePayers = new Set<string>(feePayers);
227
- await this.#evictionManager.evictAfterNewTxs(Array.from(acceptedPending), [...uniqueFeePayers]);
279
+ // Record metrics
280
+ if (ignored.length > 0) {
281
+ this.#instrumentation.recordIgnored(ignored.length);
228
282
  }
229
-
230
- // Emit events
231
- if (newlyAdded.length > 0) {
232
- this.#callbacks.onTxsAdded(newlyAdded, opts);
283
+ if (rejected.length > 0) {
284
+ this.#instrumentation.recordRejected(rejected.length);
233
285
  }
234
286
 
235
- return { accepted, ignored, rejected };
287
+ return { accepted, ignored, rejected, ...(errors.size > 0 ? { errors } : {}) };
236
288
  }
237
289
 
238
- /** Validates and adds a regular pending tx. Returns status. */
290
+ /** Adds a validated pending tx, running pre-add rules and evicting conflicts. */
239
291
  async #tryAddRegularPendingTx(
240
292
  tx: Tx,
293
+ precomputedMeta: TxMetaData,
294
+ opts: { source?: string },
241
295
  poolAccess: PreAddPoolAccess,
242
296
  acceptedPending: Set<string>,
243
297
  ignored: TxHash[],
244
- ): Promise<{ status: 'accepted' | 'ignored' | 'rejected' }> {
245
- const txHash = tx.getTxHash();
246
- const txHashStr = txHash.toString();
298
+ errors: Map<string, TxPoolRejectionError>,
299
+ preAddContext?: PreAddContext,
300
+ ): Promise<{ status: 'accepted' | 'ignored' }> {
301
+ const txHashStr = tx.getTxHash().toString();
247
302
 
248
- // Validate transaction
249
- const validationResult = await this.#pendingTxValidator.validateTx(tx);
250
- if (validationResult.result !== 'valid') {
251
- this.#log.info(`Rejecting tx ${txHashStr}: ${validationResult.reason?.join(', ')}`);
252
- return { status: 'rejected' };
253
- }
254
-
255
- // Build metadata and run pre-add rules
256
- const meta = await buildTxMetaData(tx);
257
- const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
303
+ // Run pre-add rules
304
+ const preAddResult = await this.#evictionManager.runPreAddRules(precomputedMeta, poolAccess, preAddContext);
258
305
 
259
306
  if (preAddResult.shouldIgnore) {
260
- this.#log.debug(`Ignoring tx ${txHashStr}: ${preAddResult.reason}`);
307
+ this.#log.debug(`Ignoring tx ${txHashStr}: ${preAddResult.reason?.message ?? 'unknown reason'}`);
308
+ if (preAddResult.reason && preAddResult.reason.code !== TxPoolRejectionCode.INTERNAL_ERROR) {
309
+ errors.set(txHashStr, preAddResult.reason);
310
+ }
261
311
  return { status: 'ignored' };
262
312
  }
263
313
 
264
- // Evict conflicts (tracking intra-batch evictions)
265
- for (const evictHashStr of preAddResult.txHashesToEvict) {
266
- await this.#deleteTx(evictHashStr);
267
- this.#log.debug(`Evicted tx ${evictHashStr} due to higher-fee tx ${txHashStr}`);
268
- if (acceptedPending.has(evictHashStr)) {
269
- acceptedPending.delete(evictHashStr);
270
- ignored.push(TxHash.fromString(evictHashStr));
314
+ // Evict conflicts, grouped by rule name for metrics
315
+ if (preAddResult.evictions && preAddResult.evictions.length > 0) {
316
+ const byReason = new Map<string, string[]>();
317
+ for (const { txHash: evictHash, reason } of preAddResult.evictions) {
318
+ const group = byReason.get(reason);
319
+ if (group) {
320
+ group.push(evictHash);
321
+ } else {
322
+ byReason.set(reason, [evictHash]);
323
+ }
324
+ }
325
+ for (const [reason, hashes] of byReason) {
326
+ await this.#evictTxs(hashes, reason);
327
+ }
328
+ for (const evictHashStr of preAddResult.txHashesToEvict) {
329
+ this.#log.debug(`Evicted tx ${evictHashStr} due to higher-fee tx ${txHashStr}`, {
330
+ evictedTxHash: evictHashStr,
331
+ replacementTxHash: txHashStr,
332
+ });
333
+ if (acceptedPending.has(evictHashStr)) {
334
+ // Evicted tx was from this batch - mark as ignored in result
335
+ acceptedPending.delete(evictHashStr);
336
+ ignored.push(TxHash.fromString(evictHashStr));
337
+ }
271
338
  }
272
339
  }
273
340
 
341
+ // Randomly drop the transaction for testing purposes (report as accepted so it propagates)
342
+ if (this.#config.dropTransactionsProbability > 0 && Math.random() < this.#config.dropTransactionsProbability) {
343
+ this.#log.debug(`Dropping tx ${txHashStr} (simulated drop for testing)`);
344
+ return { status: 'accepted' };
345
+ }
346
+
274
347
  // Add the transaction
275
- await this.#addNewPendingTx(tx);
348
+ await this.#addTx(tx, 'pending', opts, precomputedMeta);
276
349
  return { status: 'accepted' };
277
350
  }
278
351
 
279
- async canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored' | 'rejected'> {
352
+ async canAddPendingTx(tx: Tx): Promise<'accepted' | 'ignored'> {
280
353
  const txHashStr = tx.getTxHash().toString();
281
354
 
282
355
  // Check if already in pool
283
- if (this.#metadata.has(txHashStr)) {
356
+ if (this.#indices.has(txHashStr)) {
357
+ this.#log.verbose(`canAddPendingTx: tx ${txHashStr} already in pool`);
284
358
  return 'ignored';
285
359
  }
286
360
 
287
- // Validate transaction
288
- const validationResult = await this.#pendingTxValidator.validateTx(tx);
289
- if (validationResult.result !== 'valid') {
290
- return 'rejected';
291
- }
292
-
293
- // Build metadata and use pre-add rules
361
+ // Build metadata and check pre-add rules
294
362
  const meta = await buildTxMetaData(tx);
295
363
  const poolAccess = this.#createPreAddPoolAccess();
296
364
  const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
297
365
 
298
- return preAddResult.shouldIgnore ? 'ignored' : 'accepted';
366
+ if (preAddResult.shouldIgnore) {
367
+ this.#log.verbose(`canAddPendingTx: tx ${txHashStr} ignored by pre-add rule`, {
368
+ reason: preAddResult.reason?.message ?? 'no reason provided',
369
+ });
370
+ return 'ignored';
371
+ }
372
+ return 'accepted';
299
373
  }
300
374
 
301
375
  async addProtectedTxs(txs: Tx[], block: BlockHeader, opts: { source?: string }): Promise<void> {
302
376
  const slotNumber = block.globalVariables.slotNumber;
303
- const newlyAdded: Tx[] = [];
304
377
 
305
378
  await this.#store.transactionAsync(async () => {
306
379
  for (const tx of txs) {
307
380
  const txHash = tx.getTxHash();
308
381
  const txHashStr = txHash.toString();
309
- const isNew = !this.#metadata.has(txHashStr);
382
+ const isNew = !this.#indices.has(txHashStr);
310
383
  const minedBlockId = await this.#getMinedBlockId(txHash);
311
384
 
312
385
  if (isNew) {
313
- // New tx - add as mined or protected
386
+ // New tx - add as mined or protected (callback emitted by #addTx)
314
387
  if (minedBlockId) {
315
- await this.#addNewMinedTx(tx, minedBlockId);
316
- this.#protectedTransactions.set(txHashStr, slotNumber);
388
+ await this.#addTx(tx, { mined: minedBlockId }, opts);
389
+ this.#indices.setProtection(txHashStr, slotNumber);
317
390
  } else {
318
- await this.#addNewProtectedTx(tx, slotNumber);
391
+ await this.#addTx(tx, { protected: slotNumber }, opts);
319
392
  }
320
- newlyAdded.push(tx);
321
393
  } else {
322
394
  // Existing tx - update protection and mined status
323
- this.#updateProtection(txHashStr, slotNumber);
395
+ this.#indices.updateProtection(txHashStr, slotNumber);
324
396
  if (minedBlockId) {
325
- this.#markAsMined(this.#metadata.get(txHashStr)!, minedBlockId);
397
+ const meta = this.#indices.getMetadata(txHashStr)!;
398
+ this.#indices.markAsMined(meta, minedBlockId);
326
399
  }
327
400
  }
328
401
  }
329
402
  });
330
-
331
- if (newlyAdded.length > 0) {
332
- this.#callbacks.onTxsAdded(newlyAdded, opts);
333
- }
334
403
  }
335
404
 
336
- protectTxs(txHashes: TxHash[], block: BlockHeader): TxHash[] {
405
+ async protectTxs(txHashes: TxHash[], block: BlockHeader): Promise<TxHash[]> {
337
406
  const slotNumber = block.globalVariables.slotNumber;
338
407
  const missing: TxHash[] = [];
408
+ let softDeletedHits = 0;
409
+ let missingPreviouslyEvicted = 0;
339
410
 
340
- for (const txHash of txHashes) {
341
- const txHashStr = txHash.toString();
411
+ await this.#store.transactionAsync(async () => {
412
+ for (const txHash of txHashes) {
413
+ const txHashStr = txHash.toString();
342
414
 
343
- if (this.#metadata.has(txHashStr)) {
344
- // Step 1a: Update protection for existing tx
345
- this.#updateProtection(txHashStr, slotNumber);
346
- } else {
347
- // Step 1b: Pre-record protection for tx we don't have yet
348
- this.#protectedTransactions.set(txHashStr, slotNumber);
349
- missing.push(txHash);
415
+ if (this.#indices.has(txHashStr)) {
416
+ // Update protection for existing tx
417
+ this.#indices.updateProtection(txHashStr, slotNumber);
418
+ } else if (this.#deletedPool.isSoftDeleted(txHashStr)) {
419
+ // Resurrect soft-deleted tx as protected
420
+ const buffer = await this.#txsDB.getAsync(txHashStr);
421
+ if (buffer) {
422
+ const tx = Tx.fromBuffer(buffer);
423
+ await this.#addTx(tx, { protected: slotNumber });
424
+ softDeletedHits++;
425
+ } else {
426
+ // Data missing despite soft-delete flag — treat as truly missing
427
+ this.#indices.setProtection(txHashStr, slotNumber);
428
+ missing.push(txHash);
429
+ }
430
+ } else {
431
+ // Truly missing — pre-record protection for tx we don't have yet
432
+ this.#indices.setProtection(txHashStr, slotNumber);
433
+ missing.push(txHash);
434
+ if (this.#evictedTxHashes.has(txHashStr)) {
435
+ missingPreviouslyEvicted++;
436
+ }
437
+ }
350
438
  }
439
+ });
440
+
441
+ // Record metrics
442
+ if (softDeletedHits > 0) {
443
+ this.#instrumentation.recordSoftDeletedHits(softDeletedHits);
444
+ }
445
+ if (missing.length > 0) {
446
+ this.#log.debug(`protectTxs missing tx hashes: ${missing.map(h => h.toString()).join(', ')}`);
447
+ this.#instrumentation.recordMissingOnProtect(missing.length);
351
448
  }
449
+ if (missingPreviouslyEvicted > 0) {
450
+ this.#instrumentation.recordMissingPreviouslyEvicted(missingPreviouslyEvicted);
451
+ }
452
+
453
+ this.#log.info(
454
+ `Protected ${txHashes.length} txs, missing: ${missing.length}, soft-deleted hits: ${softDeletedHits}`,
455
+ );
352
456
 
353
457
  return missing;
354
458
  }
@@ -356,28 +460,22 @@ export class TxPoolV2Impl {
356
460
  async addMinedTxs(txs: Tx[], block: BlockHeader, opts: { source?: string }): Promise<void> {
357
461
  // Step 1: Build block ID
358
462
  const blockId = await this.#buildBlockId(block);
359
- const newlyAdded: Tx[] = [];
360
463
 
361
464
  await this.#store.transactionAsync(async () => {
362
465
  for (const tx of txs) {
363
466
  const txHashStr = tx.getTxHash().toString();
364
- const existingMeta = this.#metadata.get(txHashStr);
467
+ const existingMeta = this.#indices.getMetadata(txHashStr);
365
468
 
366
469
  if (existingMeta) {
367
- // Step 2a: Mark existing tx as mined
368
- this.#markAsMined(existingMeta, blockId);
470
+ // Mark existing tx as mined
471
+ this.#indices.markAsMined(existingMeta, blockId);
369
472
  } else {
370
- // Step 2b: Add new mined tx
371
- await this.#addNewMinedTx(tx, blockId);
372
- newlyAdded.push(tx);
473
+ // Add new mined tx (callback emitted by #addTx)
474
+ await this.#addTx(tx, { mined: blockId }, opts);
373
475
  }
476
+ await this.#deletedPool.clearIfMinedHigher(txHashStr, blockId.number);
374
477
  }
375
478
  });
376
-
377
- // Step 3: Emit events for newly added txs
378
- if (newlyAdded.length > 0) {
379
- this.#callbacks.onTxsAdded(newlyAdded, opts);
380
- }
381
479
  }
382
480
 
383
481
  async handleMinedBlock(block: L2Block): Promise<void> {
@@ -392,61 +490,76 @@ export class TxPoolV2Impl {
392
490
  const feePayers: string[] = [];
393
491
  const found: TxMetaData[] = [];
394
492
  for (const txHash of txHashes) {
395
- const meta = this.#metadata.get(txHash.toString());
493
+ const meta = this.#indices.getMetadata(txHash.toString());
396
494
  if (meta) {
397
495
  feePayers.push(meta.feePayer);
398
496
  found.push(meta);
399
497
  }
400
498
  }
401
499
 
402
- // Step 4: Mark txs as mined (only those we have in the pool)
403
- this.#markTxsAsMined(found, blockId);
500
+ await this.#store.transactionAsync(async () => {
501
+ // Step 4: Mark txs as mined (only those we have in the pool)
502
+ for (const meta of found) {
503
+ this.#indices.markAsMined(meta, blockId);
504
+ await this.#deletedPool.clearIfMinedHigher(meta.txHash, blockId.number);
505
+ }
404
506
 
405
- // Step 5: Run eviction rules (remove pending txs with conflicting nullifiers/expired timestamps)
406
- await this.#evictionManager.evictAfterNewBlock(block.header, nullifiers, feePayers);
507
+ // Step 5: Run post-event eviction rules (inside transaction for atomicity)
508
+ await this.#evictionManager.evictAfterNewBlock(block.header, nullifiers, feePayers);
509
+ });
510
+
511
+ if (found.length > 0) {
512
+ this.#callbacks.onTxsMined(found.map(m => m.txHash));
513
+ }
407
514
 
408
- this.#callbacks.onTxsRemoved(txHashes.map(h => h.toBigInt()));
409
515
  this.#log.info(`Marked ${found.length} txs as mined in block ${blockId.number}`);
410
516
  }
411
517
 
412
518
  async prepareForSlot(slotNumber: SlotNumber): Promise<void> {
413
- // Step 1: Find expired protected txs
414
- const expiredProtected = this.#findExpiredProtectedTxs(slotNumber);
519
+ await this.#store.transactionAsync(async () => {
520
+ // Step 0: Clean up slot-deleted txs from previous slots
521
+ await this.#deletedPool.cleanupSlotDeleted(slotNumber);
415
522
 
416
- // Step 2: Clear protection for all expired entries (including those without metadata)
417
- this.#clearProtection(expiredProtected);
523
+ // Step 1: Find expired protected txs
524
+ const expiredProtected = this.#indices.findExpiredProtectedTxs(slotNumber);
418
525
 
419
- // Step 3: Filter to only txs that have metadata and are not mined
420
- const txsToRestore = this.#filterRestorable(expiredProtected);
421
- if (txsToRestore.length === 0) {
422
- return;
423
- }
526
+ // Step 2: Clear protection for all expired entries (including those without metadata)
527
+ this.#indices.clearProtection(expiredProtected);
424
528
 
425
- this.#log.info(`Preparing for slot ${slotNumber}: unprotecting ${txsToRestore.length} txs`);
529
+ // Step 3: Filter to only txs that have metadata and are not mined
530
+ const txsToRestore = this.#indices.filterRestorable(expiredProtected);
531
+ if (txsToRestore.length === 0) {
532
+ this.#log.debug(`Preparing for slot ${slotNumber}, no txs to unprotect`);
533
+ return;
534
+ }
426
535
 
427
- // Step 4: Validate for pending pool
428
- const { valid, invalid } = await this.#validateForPending(txsToRestore);
536
+ this.#log.info(`Preparing for slot ${slotNumber}: unprotecting ${txsToRestore.length} txs`);
429
537
 
430
- // Step 5: Resolve nullifier conflicts and add winners to pending indices
431
- const { added, toEvict } = this.#applyNullifierConflictResolution(valid);
538
+ // Step 4: Validate for pending pool
539
+ const { valid, invalid } = await this.#revalidateMetadata(txsToRestore, 'during prepareForSlot');
432
540
 
433
- // Step 6: Delete invalid and evicted txs
434
- await this.#deleteTxsBatch([...invalid, ...toEvict]);
541
+ // Step 5: Resolve nullifier conflicts and add winners to pending indices
542
+ const { added, toEvict } = this.#applyNullifierConflictResolution(valid);
435
543
 
436
- // Step 7: Run eviction rules (enforce pool size limit)
437
- if (added.length > 0) {
438
- const feePayers = added.map(meta => meta.feePayer);
439
- const uniqueFeePayers = new Set<string>(feePayers);
440
- await this.#evictionManager.evictAfterNewTxs(
441
- added.map(m => m.txHash),
442
- [...uniqueFeePayers],
443
- );
444
- }
544
+ // Step 6: Delete invalid txs and evict conflict losers
545
+ await this.#deleteTxsBatch(invalid);
546
+ await this.#evictTxs(toEvict, 'NullifierConflict');
547
+
548
+ // Step 7: Run eviction rules (enforce pool size limit)
549
+ if (added.length > 0) {
550
+ const feePayers = added.map(meta => meta.feePayer);
551
+ const uniqueFeePayers = new Set<string>(feePayers);
552
+ await this.#evictionManager.evictAfterNewTxs(
553
+ added.map(m => m.txHash),
554
+ [...uniqueFeePayers],
555
+ );
556
+ }
557
+ });
445
558
  }
446
559
 
447
- async handlePrunedBlocks(latestBlock: L2BlockId): Promise<void> {
560
+ async handlePrunedBlocks(latestBlock: L2BlockId, options?: { deleteAllTxs?: boolean }): Promise<void> {
448
561
  // Step 1: Find transactions mined after the prune point
449
- const txsToUnmine = this.#findTxsMinedAfter(latestBlock.number);
562
+ const txsToUnmine = this.#indices.findTxsMinedAfter(latestBlock.number);
450
563
  if (txsToUnmine.length === 0) {
451
564
  this.#log.debug(`No transactions to un-mine for prune to block ${latestBlock.number}`);
452
565
  return;
@@ -454,62 +567,99 @@ export class TxPoolV2Impl {
454
567
 
455
568
  this.#log.info(`Handling prune to block ${latestBlock.number}: un-mining ${txsToUnmine.length} txs`);
456
569
 
457
- // Step 2: Unmine - clear mined status from metadata
458
- this.#unmineTxs(txsToUnmine);
570
+ await this.#store.transactionAsync(async () => {
571
+ // Step 2: Mark ALL un-mined txs with their original mined block number
572
+ // This ensures they get soft-deleted if removed later, and only hard-deleted
573
+ // when their original mined block is finalized
574
+ await this.#deletedPool.markFromPrunedBlock(
575
+ txsToUnmine.map(m => ({
576
+ txHash: m.txHash,
577
+ minedAtBlock: BlockNumber(m.minedL2BlockId!.number),
578
+ })),
579
+ );
580
+
581
+ // Step 3: Unmine - clear mined status from metadata
582
+ for (const meta of txsToUnmine) {
583
+ this.#indices.markAsUnmined(meta);
584
+ }
459
585
 
460
- // Step 3: Filter out protected txs (they'll be handled by prepareForSlot)
461
- const unprotectedTxs = this.#filterUnprotected(txsToUnmine);
586
+ // If deleteAllTxs is set (epoch prune), delete all un-mined txs and return early
587
+ if (options?.deleteAllTxs) {
588
+ const allTxHashes = txsToUnmine.map(m => m.txHash);
589
+ await this.#deleteTxsBatch(allTxHashes);
590
+ this.#log.info(
591
+ `Handled prune to block ${latestBlock.number} with deleteAllTxs: deleted ${allTxHashes.length} txs`,
592
+ );
593
+ return;
594
+ }
462
595
 
463
- // Step 4: Validate for pending pool
464
- const { valid, invalid } = await this.#validateForPending(unprotectedTxs);
596
+ // Step 4: Filter out protected txs (they'll be handled by prepareForSlot)
597
+ const unprotectedTxs = this.#indices.filterUnprotected(txsToUnmine);
465
598
 
466
- // Step 5: Resolve nullifier conflicts and add winners to pending indices
467
- const { toEvict } = this.#applyNullifierConflictResolution(valid);
599
+ // Step 5: Validate for pending pool
600
+ const { valid, invalid } = await this.#revalidateMetadata(unprotectedTxs, 'during handlePrunedBlocks');
468
601
 
469
- // Step 6: Delete invalid and evicted txs
470
- await this.#deleteTxsBatch([...invalid, ...toEvict]);
602
+ // Step 6: Resolve nullifier conflicts and add winners to pending indices
603
+ const { toEvict } = this.#applyNullifierConflictResolution(valid);
471
604
 
472
- // Step 7: Run eviction rules for ALL pending txs (not just restored ones)
473
- // This handles cases like existing pending txs with invalid fee payer balances
474
- await this.#evictionManager.evictAfterChainPrune(latestBlock.number);
605
+ // Step 7: Delete invalid txs and evict conflict losers
606
+ await this.#deleteTxsBatch(invalid);
607
+ await this.#evictTxs(toEvict, 'NullifierConflict');
608
+
609
+ this.#log.info(
610
+ `Handled prune to block ${latestBlock.number}: ${valid.length} txs restored to pending, ${invalid.length} invalid, ${toEvict.length} evicted due to nullifier conflicts`,
611
+ { txHashesRestored: valid.map(m => m.txHash), txHashesInvalid: invalid, txHashesEvicted: toEvict },
612
+ );
613
+
614
+ // Step 8: Run eviction rules for ALL pending txs (not just restored ones)
615
+ // This handles cases like existing pending txs with invalid fee payer balances
616
+ await this.#evictionManager.evictAfterChainPrune(latestBlock.number);
617
+ });
475
618
  }
476
619
 
477
620
  async handleFailedExecution(txHashes: TxHash[]): Promise<void> {
478
- // Step 1: Delete failed txs
479
- await this.#deleteTxsBatch(txHashes.map(h => h.toString()));
621
+ await this.#store.transactionAsync(async () => {
622
+ await this.#deleteTxsBatch(txHashes.map(h => h.toString()));
623
+ });
480
624
 
481
- this.#log.info(`Deleted ${txHashes.length} failed txs`);
625
+ this.#log.info(`Deleted ${txHashes.length} failed txs`, { txHashes: txHashes.map(h => h.toString()) });
482
626
  }
483
627
 
484
628
  async handleFinalizedBlock(block: BlockHeader): Promise<void> {
485
629
  const blockNumber = block.globalVariables.blockNumber;
486
630
 
487
- // Step 1: Find txs mined at or before finalized block
488
- const txsToFinalize = this.#findTxsMinedAtOrBefore(blockNumber);
489
- if (txsToFinalize.length === 0) {
490
- return;
491
- }
631
+ // Step 1: Find mined txs at or before finalized block
632
+ const minedTxsToFinalize = this.#indices.findTxsMinedAtOrBefore(blockNumber);
492
633
 
493
- // Step 2: Collect txs for archiving (before deletion)
494
- const txsToArchive: Tx[] = [];
495
- if (this.#archive.isEnabled()) {
496
- for (const txHashStr of txsToFinalize) {
497
- const buffer = await this.#txsDB.getAsync(txHashStr);
498
- if (buffer) {
499
- txsToArchive.push(Tx.fromBuffer(buffer));
634
+ await this.#store.transactionAsync(async () => {
635
+ // Step 2: Collect mined txs for archiving (before deletion)
636
+ const txsToArchive: Tx[] = [];
637
+ if (this.#archive.isEnabled()) {
638
+ for (const txHashStr of minedTxsToFinalize) {
639
+ const buffer = await this.#txsDB.getAsync(txHashStr);
640
+ if (buffer) {
641
+ txsToArchive.push(Tx.fromBuffer(buffer));
642
+ }
500
643
  }
501
644
  }
502
- }
503
645
 
504
- // Step 3: Delete from active pool
505
- await this.#deleteTxsBatch(txsToFinalize);
646
+ // Step 3: Delete mined txs from active pool
647
+ await this.#deleteTxsBatch(minedTxsToFinalize);
506
648
 
507
- // Step 4: Archive
508
- if (txsToArchive.length > 0) {
509
- await this.#archive.archiveTxs(txsToArchive);
510
- }
649
+ // Step 4: Finalize soft-deleted txs
650
+ await this.#deletedPool.finalizeBlock(blockNumber);
651
+
652
+ // Step 5: Archive mined txs
653
+ if (txsToArchive.length > 0) {
654
+ await this.#archive.archiveTxs(txsToArchive);
655
+ }
656
+ });
511
657
 
512
- this.#log.info(`Finalized ${txsToFinalize.length} txs from blocks up to ${blockNumber}`);
658
+ if (minedTxsToFinalize.length > 0) {
659
+ this.#log.info(`Finalized ${minedTxsToFinalize.length} mined txs from blocks up to ${blockNumber}`, {
660
+ txHashes: minedTxsToFinalize,
661
+ });
662
+ }
513
663
  }
514
664
 
515
665
  // === Query Methods ===
@@ -529,42 +679,47 @@ export class TxPoolV2Impl {
529
679
  }
530
680
 
531
681
  hasTxs(txHashes: TxHash[]): boolean[] {
532
- return txHashes.map(h => this.#metadata.has(h.toString()));
682
+ return txHashes.map(h => {
683
+ const hashStr = h.toString();
684
+ return this.#indices.has(hashStr) || this.#deletedPool.isSoftDeleted(hashStr);
685
+ });
533
686
  }
534
687
 
535
688
  getTxStatus(txHash: TxHash): TxState | undefined {
536
- const meta = this.#metadata.get(txHash.toString());
537
- if (!meta) {
538
- return undefined;
689
+ const txHashStr = txHash.toString();
690
+ const meta = this.#indices.getMetadata(txHashStr);
691
+ if (meta) {
692
+ return this.#indices.getTxState(meta);
693
+ }
694
+ // Check if soft-deleted
695
+ if (this.#deletedPool.isSoftDeleted(txHashStr)) {
696
+ return 'deleted';
539
697
  }
540
- return this.#getTxState(meta);
698
+ return undefined;
541
699
  }
542
700
 
543
701
  getPendingTxHashes(): TxHash[] {
544
- return [...this.#iteratePendingByPriority('desc')].map(hash => TxHash.fromString(hash));
702
+ return [...this.#indices.iteratePendingByPriority('desc')].map(hash => TxHash.fromString(hash));
703
+ }
704
+
705
+ getEligiblePendingTxHashes(): TxHash[] {
706
+ const maxReceivedAt = this.#dateProvider.now() - this.#config.minTxPoolAgeMs;
707
+ return [...this.#indices.iterateEligiblePendingByPriority('desc', maxReceivedAt)].map(hash =>
708
+ TxHash.fromString(hash),
709
+ );
545
710
  }
546
711
 
547
712
  getPendingTxCount(): number {
548
- let count = 0;
549
- for (const hashes of this.#pendingByPriority.values()) {
550
- count += hashes.size;
551
- }
552
- return count;
713
+ return this.#indices.getPendingTxCount();
553
714
  }
554
715
 
555
716
  getMinedTxHashes(): [TxHash, L2BlockId][] {
556
- const result: [TxHash, L2BlockId][] = [];
557
- for (const [txHash, meta] of this.#metadata) {
558
- if (meta.minedL2BlockId !== undefined) {
559
- result.push([TxHash.fromString(txHash), meta.minedL2BlockId]);
560
- }
561
- }
562
- return result;
717
+ return this.#indices.getMinedTxs().map(([hash, blockId]) => [TxHash.fromString(hash), blockId]);
563
718
  }
564
719
 
565
720
  getMinedTxCount(): number {
566
721
  let count = 0;
567
- for (const meta of this.#metadata.values()) {
722
+ for (const [, meta] of this.#indices.iterateMetadata()) {
568
723
  if (meta.minedL2BlockId !== undefined) {
569
724
  count++;
570
725
  }
@@ -573,11 +728,11 @@ export class TxPoolV2Impl {
573
728
  }
574
729
 
575
730
  isEmpty(): boolean {
576
- return this.#metadata.size === 0;
731
+ return this.#indices.isEmpty();
577
732
  }
578
733
 
579
734
  getTxCount(): number {
580
- return this.#metadata.size;
735
+ return this.#indices.getTxCount();
581
736
  }
582
737
 
583
738
  getArchivedTxByHash(txHash: TxHash): Promise<Tx | undefined> {
@@ -585,18 +740,7 @@ export class TxPoolV2Impl {
585
740
  }
586
741
 
587
742
  getLowestPriorityPending(limit: number): TxHash[] {
588
- if (limit <= 0) {
589
- return [];
590
- }
591
-
592
- const result: TxHash[] = [];
593
- for (const hash of this.#iteratePendingByPriority('asc')) {
594
- result.push(TxHash.fromString(hash));
595
- if (result.length >= limit) {
596
- break;
597
- }
598
- }
599
- return result;
743
+ return this.#indices.getLowestPriorityPending(limit).map(h => TxHash.fromString(h));
600
744
  }
601
745
 
602
746
  // === Configuration ===
@@ -609,6 +753,9 @@ export class TxPoolV2Impl {
609
753
  this.#config.archivedTxLimit = config.archivedTxLimit;
610
754
  this.#archive.updateLimit(config.archivedTxLimit);
611
755
  }
756
+ if (config.minTxPoolAgeMs !== undefined) {
757
+ this.#config.minTxPoolAgeMs = config.minTxPoolAgeMs;
758
+ }
612
759
  // Update eviction rules with new config
613
760
  this.#evictionManager.updateConfig(config);
614
761
  }
@@ -617,159 +764,146 @@ export class TxPoolV2Impl {
617
764
 
618
765
  getPoolReadAccess(): PoolReadAccess {
619
766
  return {
620
- getMetadata: (txHash: string) => this.#metadata.get(txHash),
621
- getTxHashByNullifier: (nullifier: string) => this.#nullifierToTxHash.get(nullifier),
622
- getTxHashesByFeePayer: (feePayer: string) => this.#feePayerToTxHashes.get(feePayer),
623
- getPendingTxCount: () => this.getPendingTxCount(),
767
+ getMetadata: (txHash: string) => this.#indices.getMetadata(txHash),
768
+ getTxHashByNullifier: (nullifier: string) => this.#indices.getTxHashByNullifier(nullifier),
769
+ getTxHashesByFeePayer: (feePayer: string) => this.#indices.getTxHashesByFeePayer(feePayer),
770
+ getPendingTxCount: () => this.#indices.getPendingTxCount(),
624
771
  };
625
772
  }
626
773
 
627
774
  // === Metrics ===
628
775
 
629
- countTxs(): { pending: number; protected: number; mined: number } {
630
- let pending = 0;
631
- let protected_ = 0;
632
- let mined = 0;
633
-
634
- for (const meta of this.#metadata.values()) {
635
- const state = this.#getTxState(meta);
636
- if (state === 'pending') {
637
- pending++;
638
- } else if (state === 'protected') {
639
- protected_++;
640
- } else if (state === 'mined') {
641
- mined++;
642
- }
643
- }
644
-
645
- return { pending, protected: protected_, mined };
776
+ countTxs(): {
777
+ pending: number;
778
+ protected: number;
779
+ mined: number;
780
+ softDeleted: number;
781
+ totalMetadataBytes: number;
782
+ } {
783
+ return {
784
+ ...this.#indices.countTxs(),
785
+ softDeleted: this.#deletedPool.getSoftDeletedCount(),
786
+ };
646
787
  }
647
788
 
648
789
  // ============================================================================
649
- // PRIVATE QUERY IMPLEMENTATIONS
790
+ // PRIVATE HELPERS - Transaction Management
650
791
  // ============================================================================
651
792
 
652
793
  /**
653
- * Derives the transaction state from its metadata and protection status.
654
- * A transaction is:
655
- * - 'mined' if it has a minedL2BlockId
656
- * - 'protected' if it's in the protectedTransactions map (but not mined)
657
- * - 'pending' otherwise
794
+ * Adds a new transaction to the pool with the specified state.
795
+ * Emits onTxsAdded callback immediately after DB write.
658
796
  */
659
- #getTxState(meta: TxMetaData): TxState {
660
- if (meta.minedL2BlockId !== undefined) {
661
- return 'mined';
662
- } else if (this.#protectedTransactions.has(meta.txHash)) {
663
- return 'protected';
797
+ async #addTx(
798
+ tx: Tx,
799
+ state: 'pending' | { protected: SlotNumber } | { mined: L2BlockId },
800
+ opts: { source?: string } = {},
801
+ precomputedMeta?: TxMetaData,
802
+ ): Promise<TxMetaData> {
803
+ const txHashStr = tx.getTxHash().toString();
804
+ const meta = precomputedMeta ?? (await buildTxMetaData(tx));
805
+ meta.receivedAt = this.#dateProvider.now();
806
+
807
+ await this.#txsDB.set(txHashStr, tx.toBuffer());
808
+ await this.#deletedPool.clearSoftDeleted(txHashStr);
809
+ this.#callbacks.onTxsAdded([tx], opts);
810
+
811
+ if (state === 'pending') {
812
+ this.#indices.addPending(meta);
813
+ } else if ('protected' in state) {
814
+ this.#indices.addProtected(meta, state.protected);
664
815
  } else {
665
- return 'pending';
816
+ meta.minedL2BlockId = state.mined;
817
+ this.#indices.addMined(meta);
666
818
  }
819
+
820
+ const stateStr = typeof state === 'string' ? state : Object.keys(state)[0];
821
+ this.#log.debug(`Added tx ${txHashStr} as ${stateStr}`, {
822
+ eventName: 'tx-added-to-pool',
823
+ txHash: txHashStr,
824
+ state: stateStr,
825
+ source: opts.source,
826
+ });
827
+
828
+ return meta;
667
829
  }
668
830
 
669
831
  /**
670
- * Iterates pending transaction hashes in priority order.
671
- * @param order - 'desc' for highest priority first, 'asc' for lowest priority first
832
+ * Deletes a transaction from both indices and DB.
833
+ * Emits onTxsRemoved callback immediately after DB delete.
672
834
  */
673
- *#iteratePendingByPriority(order: 'asc' | 'desc'): Generator<string> {
674
- // Use shared comparators, negating for descending order
675
- const feeCompareFn =
676
- order === 'desc' ? (a: bigint, b: bigint) => compareFee(b, a) : (a: bigint, b: bigint) => compareFee(a, b);
677
- const hashCompareFn =
678
- order === 'desc' ? (a: string, b: string) => compareTxHash(b, a) : (a: string, b: string) => compareTxHash(a, b);
679
-
680
- const sortedFees = [...this.#pendingByPriority.keys()].sort(feeCompareFn);
681
-
682
- for (const fee of sortedFees) {
683
- const hashesAtFee = this.#pendingByPriority.get(fee)!;
684
- const sortedHashes = [...hashesAtFee].sort(hashCompareFn);
685
- for (const hash of sortedHashes) {
686
- yield hash;
687
- }
688
- }
835
+ /**
836
+ * Deletes a transaction from the pool.
837
+ * Delegates to DeletedPool which decides soft vs hard delete based on whether
838
+ * the tx is from a pruned block.
839
+ */
840
+ async #deleteTx(txHashStr: string): Promise<void> {
841
+ this.#indices.remove(txHashStr);
842
+ this.#callbacks.onTxsRemoved([txHashStr]);
843
+ await this.#deletedPool.deleteTx(txHashStr);
689
844
  }
690
845
 
691
- // ============================================================================
692
- // HELPER FUNCTIONS - Pipeline Step Functions
693
- // ============================================================================
694
-
695
- // --- Finding & Filtering Steps ---
696
-
697
- /** Finds all transactions mined in blocks after the given block number */
698
- #findTxsMinedAfter(blockNumber: number): TxMetaData[] {
699
- const result: TxMetaData[] = [];
700
- for (const meta of this.#metadata.values()) {
701
- if (meta.minedL2BlockId !== undefined && meta.minedL2BlockId.number > blockNumber) {
702
- result.push(meta);
703
- }
846
+ /** Deletes a batch of transactions, emitting callbacks individually for each. */
847
+ async #deleteTxsBatch(txHashes: string[]): Promise<void> {
848
+ for (const txHashStr of txHashes) {
849
+ await this.#deleteTx(txHashStr);
704
850
  }
705
- return result;
706
851
  }
707
852
 
708
- /** Finds tx hashes mined at or before the given block number */
709
- #findTxsMinedAtOrBefore(blockNumber: number): string[] {
710
- const result: string[] = [];
711
- for (const [txHashStr, meta] of this.#metadata) {
712
- if (meta.minedL2BlockId !== undefined && meta.minedL2BlockId.number <= blockNumber) {
713
- result.push(txHashStr);
714
- }
853
+ /** Evicts transactions: records eviction metric with reason, caches hashes, then deletes. */
854
+ async #evictTxs(txHashes: string[], reason: string): Promise<void> {
855
+ if (txHashes.length === 0) {
856
+ return;
857
+ }
858
+ this.#instrumentation.recordEvictions(txHashes.length, reason);
859
+ for (const txHashStr of txHashes) {
860
+ this.#log.debug(`Evicting tx ${txHashStr}`, { txHash: txHashStr, reason });
861
+ this.#addToEvictedCache(txHashStr);
715
862
  }
716
- return result;
863
+ await this.#deleteTxsBatch(txHashes);
717
864
  }
718
865
 
719
- /** Finds protected tx hashes from slots earlier than the given slot number */
720
- #findExpiredProtectedTxs(slotNumber: SlotNumber): string[] {
721
- const result: string[] = [];
722
- for (const [txHashStr, protectedSlot] of this.#protectedTransactions) {
723
- if (protectedSlot < slotNumber) {
724
- result.push(txHashStr);
725
- }
866
+ /** Adds a tx hash to the bounded evicted cache, evicting the oldest entry if at capacity. */
867
+ #addToEvictedCache(txHashStr: string): void {
868
+ if (this.#evictedTxHashes.size >= this.#config.evictedTxCacheSize) {
869
+ // FIFO eviction: remove the first (oldest) entry
870
+ const oldest = this.#evictedTxHashes.values().next().value!;
871
+ this.#evictedTxHashes.delete(oldest);
726
872
  }
727
- return result;
873
+ this.#evictedTxHashes.add(txHashStr);
728
874
  }
729
875
 
730
- /** Filters out transactions that are currently protected */
731
- #filterUnprotected(txs: TxMetaData[]): TxMetaData[] {
732
- return txs.filter(meta => !this.#protectedTransactions.has(meta.txHash));
733
- }
876
+ // ============================================================================
877
+ // PRIVATE HELPERS - Validation & Conflict Resolution
878
+ // ============================================================================
734
879
 
735
- /** Filters to transactions that have metadata and are not mined */
736
- #filterRestorable(txHashes: string[]): TxMetaData[] {
737
- const result: TxMetaData[] = [];
738
- for (const txHashStr of txHashes) {
739
- const meta = this.#metadata.get(txHashStr);
740
- if (meta && meta.minedL2BlockId === undefined) {
741
- result.push(meta);
742
- }
880
+ /** Validates transaction metadata, returning true if valid */
881
+ async #validateMeta(meta: TxMetaData, validator?: TxValidator<TxMetaData>, context?: string): Promise<boolean> {
882
+ const txValidator = validator ?? (await this.#createTxValidator());
883
+ const result = await txValidator.validateTx(meta);
884
+ if (result.result !== 'valid') {
885
+ const contextStr = context ? ` ${context}` : '';
886
+ this.#log.info(`Tx ${meta.txHash}${contextStr} failed validation: ${result.reason?.join(', ')}`);
887
+ return false;
743
888
  }
744
- return result;
889
+ return true;
745
890
  }
746
891
 
747
- // --- Validation & Conflict Resolution Steps ---
748
-
749
- /** Validates transactions for pending pool, returning valid and invalid groups */
750
- async #validateForPending(txs: TxMetaData[]): Promise<{ valid: TxMetaData[]; invalid: string[] }> {
892
+ /** Validates metadata directly */
893
+ async #revalidateMetadata(
894
+ metas: TxMetaData[],
895
+ context?: string,
896
+ ): Promise<{ valid: TxMetaData[]; invalid: string[] }> {
751
897
  const valid: TxMetaData[] = [];
752
898
  const invalid: string[] = [];
753
-
754
- for (const meta of txs) {
755
- const buffer = await this.#txsDB.getAsync(meta.txHash);
756
- if (!buffer) {
757
- this.#log.warn(`Tx ${meta.txHash} not found in DB during validation`);
758
- invalid.push(meta.txHash);
759
- continue;
760
- }
761
-
762
- const tx = Tx.fromBuffer(buffer);
763
- const result = await this.#pendingTxValidator.validateTx(tx);
764
-
765
- if (result.result === 'valid') {
899
+ const validator = await this.#createTxValidator();
900
+ for (const meta of metas) {
901
+ if (await this.#validateMeta(meta, validator, context)) {
766
902
  valid.push(meta);
767
903
  } else {
768
- this.#log.info(`Tx ${meta.txHash} failed validation: ${result.reason?.join(', ')}`);
769
904
  invalid.push(meta.txHash);
770
905
  }
771
906
  }
772
-
773
907
  return { valid, invalid };
774
908
  }
775
909
 
@@ -785,8 +919,8 @@ export class TxPoolV2Impl {
785
919
  for (const meta of txs) {
786
920
  const conflict = checkNullifierConflict(
787
921
  meta,
788
- nullifier => this.#nullifierToTxHash.get(nullifier),
789
- txHash => this.#metadata.get(txHash),
922
+ nullifier => this.#indices.getTxHashByNullifier(nullifier),
923
+ txHash => this.#indices.getMetadata(txHash),
790
924
  );
791
925
  if (conflict.shouldIgnore) {
792
926
  // Lower priority than existing - don't add, mark for deletion
@@ -796,13 +930,13 @@ export class TxPoolV2Impl {
796
930
  toEvict.push(...conflict.txHashesToEvict);
797
931
  // Remove evicted from indices immediately for subsequent checks
798
932
  for (const evictHash of conflict.txHashesToEvict) {
799
- const evictMeta = this.#metadata.get(evictHash);
933
+ const evictMeta = this.#indices.getMetadata(evictHash);
800
934
  if (evictMeta) {
801
- this.#removeFromPendingIndices(evictMeta);
935
+ this.#indices.removeFromPendingIndices(evictMeta);
802
936
  }
803
937
  }
804
938
  // Add to pending indices immediately so subsequent txs in the batch see this tx
805
- this.#addToPendingIndices(meta);
939
+ this.#indices.addToPendingIndices(meta);
806
940
  added.push(meta);
807
941
  }
808
942
  }
@@ -810,43 +944,10 @@ export class TxPoolV2Impl {
810
944
  return { added, toEvict };
811
945
  }
812
946
 
813
- // --- State Transition Steps ---
814
-
815
- /** Clears the mined status from transactions, returning them for further processing */
816
- #unmineTxs(txs: TxMetaData[]): TxMetaData[] {
817
- for (const meta of txs) {
818
- meta.minedL2BlockId = undefined;
819
- }
820
- return txs;
821
- }
822
-
823
- /** Removes protection from tx hashes and clears them from the protected map */
824
- #clearProtection(txHashes: string[]): void {
825
- for (const txHashStr of txHashes) {
826
- this.#protectedTransactions.delete(txHashStr);
827
- }
828
- }
829
-
830
- // --- Batch Operation Steps ---
831
-
832
- /** Deletes a batch of transactions permanently */
833
- async #deleteTxsBatch(txHashes: string[]): Promise<void> {
834
- if (txHashes.length === 0) {
835
- return;
836
- }
837
-
838
- await this.#store.transactionAsync(async () => {
839
- for (const txHashStr of txHashes) {
840
- await this.#deleteTx(txHashStr);
841
- }
842
- });
843
-
844
- this.#callbacks.onTxsRemoved(txHashes);
845
- }
846
-
847
- // --- Block & Tx Info Steps ---
947
+ // ============================================================================
948
+ // PRIVATE HELPERS - Block & Hydration
949
+ // ============================================================================
848
950
 
849
- /** Builds a block ID from a block header */
850
951
  async #buildBlockId(block: BlockHeader): Promise<L2BlockId> {
851
952
  return {
852
953
  number: block.globalVariables.blockNumber,
@@ -866,50 +967,6 @@ export class TxPoolV2Impl {
866
967
  };
867
968
  }
868
969
 
869
- /** Marks a batch of transactions as mined */
870
- #markTxsAsMined(metas: TxMetaData[], blockId: L2BlockId): void {
871
- for (const meta of metas) {
872
- this.#markAsMined(meta, blockId);
873
- }
874
- }
875
-
876
- // --- Add Transaction Steps ---
877
-
878
- /** Persists a transaction to the database */
879
- async #persistTx(txHashStr: string, tx: Tx): Promise<void> {
880
- await this.#txsDB.set(txHashStr, tx.toBuffer());
881
- }
882
-
883
- /** Adds a new transaction as protected, returning its metadata */
884
- async #addNewProtectedTx(tx: Tx, slotNumber: SlotNumber): Promise<TxMetaData> {
885
- const txHashStr = tx.getTxHash().toString();
886
- const meta = await buildTxMetaData(tx);
887
-
888
- this.#protectedTransactions.set(txHashStr, slotNumber);
889
- await this.#persistTx(txHashStr, tx);
890
- this.#metadata.set(txHashStr, meta);
891
- // Don't add to pending indices since it's protected
892
-
893
- this.#log.verbose(`Added protected tx ${txHashStr} for slot ${slotNumber}`);
894
- return meta;
895
- }
896
-
897
- /** Adds a new transaction as mined, returning its metadata */
898
- async #addNewMinedTx(tx: Tx, blockId: L2BlockId): Promise<TxMetaData> {
899
- const txHashStr = tx.getTxHash().toString();
900
- const meta = await buildTxMetaData(tx);
901
- meta.minedL2BlockId = blockId;
902
-
903
- await this.#persistTx(txHashStr, tx);
904
- this.#metadata.set(txHashStr, meta);
905
- // Don't add to pending indices since it's mined
906
-
907
- this.#log.verbose(`Added mined tx ${txHashStr} from block ${blockId.number}`);
908
- return meta;
909
- }
910
-
911
- // --- Hydration Steps ---
912
-
913
970
  /** Loads all transactions from the database, returning loaded txs and deserialization errors */
914
971
  async #loadAllTxsFromDb(): Promise<{
915
972
  loaded: { tx: Tx; meta: TxMetaData }[];
@@ -919,6 +976,11 @@ export class TxPoolV2Impl {
919
976
  const errors: string[] = [];
920
977
 
921
978
  for await (const [txHashStr, buffer] of this.#txsDB.entriesAsync()) {
979
+ // Skip soft-deleted transactions - they stay in DB but not in indices
980
+ if (this.#deletedPool.isSoftDeleted(txHashStr)) {
981
+ continue;
982
+ }
983
+
922
984
  try {
923
985
  const tx = Tx.fromBuffer(buffer);
924
986
  const meta = await buildTxMetaData(tx);
@@ -949,50 +1011,6 @@ export class TxPoolV2Impl {
949
1011
  }
950
1012
  }
951
1013
 
952
- /** Partitions transactions by mined status */
953
- #partitionByMinedStatus(txs: { tx: Tx; meta: TxMetaData }[]): {
954
- mined: TxMetaData[];
955
- nonMined: { tx: Tx; meta: TxMetaData }[];
956
- } {
957
- const mined: TxMetaData[] = [];
958
- const nonMined: { tx: Tx; meta: TxMetaData }[] = [];
959
-
960
- for (const entry of txs) {
961
- if (entry.meta.minedL2BlockId !== undefined) {
962
- mined.push(entry.meta);
963
- } else {
964
- nonMined.push(entry);
965
- }
966
- }
967
-
968
- return { mined, nonMined };
969
- }
970
-
971
- /** Validates non-mined transactions, returning valid metadata and invalid hashes */
972
- async #validateNonMinedTxs(txs: { tx: Tx; meta: TxMetaData }[]): Promise<{ valid: TxMetaData[]; invalid: string[] }> {
973
- const valid: TxMetaData[] = [];
974
- const invalid: string[] = [];
975
-
976
- for (const { tx, meta } of txs) {
977
- const result = await this.#pendingTxValidator.validateTx(tx);
978
- if (result.result === 'valid') {
979
- valid.push(meta);
980
- } else {
981
- this.#log.info(`Removing invalid tx ${meta.txHash} on startup: ${result.reason?.join(', ')}`);
982
- invalid.push(meta.txHash);
983
- }
984
- }
985
-
986
- return { valid, invalid };
987
- }
988
-
989
- /** Populates metadata index for mined transactions */
990
- #populateMinedIndices(metas: TxMetaData[]): void {
991
- for (const meta of metas) {
992
- this.#metadata.set(meta.txHash, meta);
993
- }
994
- }
995
-
996
1014
  /**
997
1015
  * Rebuilds the pending pool by processing each tx through pre-add rules.
998
1016
  * Starts with an empty pending pool and adds txs one by one, resolving conflicts.
@@ -1010,24 +1028,26 @@ export class TxPoolV2Impl {
1010
1028
  if (preAddResult.shouldIgnore) {
1011
1029
  // Transaction rejected - mark for deletion from DB
1012
1030
  rejected.push(meta.txHash);
1013
- this.#log.debug(`Rejected tx ${meta.txHash} during rebuild: ${preAddResult.reason}`);
1031
+ this.#log.debug(
1032
+ `Rejected tx ${meta.txHash} during rebuild: ${preAddResult.reason?.message ?? 'unknown reason'}`,
1033
+ );
1014
1034
  continue;
1015
1035
  }
1016
1036
 
1017
1037
  // Evict any conflicting txs identified by pre-add rules
1018
1038
  for (const evictHashStr of preAddResult.txHashesToEvict) {
1019
- const evictMeta = this.#metadata.get(evictHashStr);
1039
+ const evictMeta = this.#indices.getMetadata(evictHashStr);
1020
1040
  if (evictMeta) {
1021
- this.#removeFromPendingIndices(evictMeta);
1022
- this.#metadata.delete(evictHashStr);
1041
+ this.#indices.removeFromPendingIndices(evictMeta);
1042
+ this.#indices.remove(evictHashStr);
1023
1043
  rejected.push(evictHashStr);
1024
1044
  accepted.delete(evictHashStr);
1025
1045
  this.#log.debug(`Evicted tx ${evictHashStr} during rebuild due to conflict with ${meta.txHash}`);
1026
1046
  }
1027
1047
  }
1028
1048
 
1029
- // Add to metadata and pending indices
1030
- this.#addToIndices(meta);
1049
+ // Add to indices
1050
+ this.#indices.addPending(meta);
1031
1051
  accepted.add(meta.txHash);
1032
1052
  }
1033
1053
 
@@ -1035,207 +1055,32 @@ export class TxPoolV2Impl {
1035
1055
  return { accepted: [...accepted], rejected };
1036
1056
  }
1037
1057
 
1038
- // --- Add Pending Tx Steps ---
1039
-
1040
- /** Checks if a tx is a duplicate (already in pool) */
1041
- #isDuplicateTx(txHashStr: string): boolean {
1042
- return this.#metadata.has(txHashStr);
1043
- }
1044
-
1045
- /** Adds a new pending tx to the pool, returning its metadata */
1046
- async #addNewPendingTx(tx: Tx): Promise<TxMetaData> {
1047
- const txHashStr = tx.getTxHash().toString();
1048
- const meta = await buildTxMetaData(tx);
1049
-
1050
- await this.#persistTx(txHashStr, tx);
1051
- this.#addToIndices(meta);
1052
-
1053
- this.#log.verbose(`Added tx ${txHashStr} to pool`, {
1054
- eventName: 'tx-added-to-pool',
1055
- state: this.#getTxState(meta),
1056
- });
1057
-
1058
- return meta;
1059
- }
1060
-
1061
1058
  // ============================================================================
1062
- // HELPER FUNCTIONS - Index Management
1059
+ // PRIVATE HELPERS - Pool Access Adapters
1063
1060
  // ============================================================================
1064
1061
 
1065
- #addToIndices(meta: TxMetaData): void {
1066
- this.#metadata.set(meta.txHash, meta);
1067
-
1068
- if (this.#getTxState(meta) === 'pending') {
1069
- this.#addToPendingIndices(meta);
1070
- }
1071
- // Protected and mined txs don't go into pending indices
1072
- }
1073
-
1074
- #addToPendingIndices(meta: TxMetaData): void {
1075
- // Add to nullifier index
1076
- for (const nullifier of meta.nullifiers) {
1077
- this.#nullifierToTxHash.set(nullifier, meta.txHash);
1078
- }
1079
-
1080
- // Add to fee payer index
1081
- let feePayerSet = this.#feePayerToTxHashes.get(meta.feePayer);
1082
- if (!feePayerSet) {
1083
- feePayerSet = new Set();
1084
- this.#feePayerToTxHashes.set(meta.feePayer, feePayerSet);
1085
- }
1086
- feePayerSet.add(meta.txHash);
1087
-
1088
- // Add to priority bucket
1089
- let prioritySet = this.#pendingByPriority.get(meta.priorityFee);
1090
- if (!prioritySet) {
1091
- prioritySet = new Set();
1092
- this.#pendingByPriority.set(meta.priorityFee, prioritySet);
1093
- }
1094
- prioritySet.add(meta.txHash);
1095
- }
1096
-
1097
- #removeFromPendingIndices(meta: TxMetaData): void {
1098
- // Remove from nullifier index
1099
- for (const nullifier of meta.nullifiers) {
1100
- this.#nullifierToTxHash.delete(nullifier);
1101
- }
1102
-
1103
- // Remove from fee payer index
1104
- const feePayerSet = this.#feePayerToTxHashes.get(meta.feePayer);
1105
- if (feePayerSet) {
1106
- feePayerSet.delete(meta.txHash);
1107
- if (feePayerSet.size === 0) {
1108
- this.#feePayerToTxHashes.delete(meta.feePayer);
1109
- }
1110
- }
1111
-
1112
- // Remove from priority map
1113
- const hashSet = this.#pendingByPriority.get(meta.priorityFee);
1114
- if (hashSet) {
1115
- hashSet.delete(meta.txHash);
1116
- if (hashSet.size === 0) {
1117
- this.#pendingByPriority.delete(meta.priorityFee);
1118
- }
1119
- }
1120
- }
1121
-
1122
- #updateProtection(txHashStr: string, slotNumber: SlotNumber): void {
1123
- const currentSlot = this.#protectedTransactions.get(txHashStr);
1124
-
1125
- // Only update if not already protected at an equal or later slot
1126
- if (currentSlot !== undefined && currentSlot >= slotNumber) {
1127
- return;
1128
- }
1129
-
1130
- // Remove from pending indices if transitioning from pending to protected
1131
- if (currentSlot === undefined) {
1132
- const meta = this.#metadata.get(txHashStr);
1133
- if (meta) {
1134
- this.#removeFromPendingIndices(meta);
1135
- }
1136
- }
1137
-
1138
- this.#protectedTransactions.set(txHashStr, slotNumber);
1139
- }
1140
-
1141
- #markAsMined(meta: TxMetaData, blockId: L2BlockId): void {
1142
- meta.minedL2BlockId = blockId;
1143
- // Safe to call unconditionally - removeFromPendingIndices is idempotent
1144
- this.#removeFromPendingIndices(meta);
1145
- }
1146
-
1147
- async #deleteTx(txHashStr: string): Promise<void> {
1148
- const meta = this.#metadata.get(txHashStr);
1149
- if (!meta) {
1150
- return;
1151
- }
1152
-
1153
- // Remove from all indices
1154
- this.#metadata.delete(txHashStr);
1155
- this.#protectedTransactions.delete(txHashStr);
1156
- this.#removeFromPendingIndices(meta);
1157
-
1158
- // Remove from persistence
1159
- await this.#txsDB.delete(txHashStr);
1160
- }
1161
-
1162
- // ============================================================================
1163
- // HELPER FUNCTIONS - Adapters
1164
- // ============================================================================
1165
-
1166
- /** Gets all pending transactions for a given fee payer. */
1167
- #getFeePayerPendingTxs(feePayer: string): TxMetaData[] {
1168
- const txHashes = this.#feePayerToTxHashes.get(feePayer);
1169
- if (!txHashes) {
1170
- return [];
1171
- }
1172
- const result: TxMetaData[] = [];
1173
- for (const txHashStr of txHashes) {
1174
- const meta = this.#metadata.get(txHashStr);
1175
- if (meta && this.#getTxState(meta) === 'pending') {
1176
- result.push(meta);
1177
- }
1178
- }
1179
- return result;
1180
- }
1181
-
1182
- /**
1183
- * Creates a PoolOperations adapter for use with the eviction manager.
1184
- */
1185
1062
  #createPoolOperations(): PoolOperations {
1186
1063
  return {
1187
- getPendingTxs: (): TxMetaData[] => {
1188
- const result: TxMetaData[] = [];
1189
- for (const hashSet of this.#pendingByPriority.values()) {
1190
- for (const txHashStr of hashSet) {
1191
- const meta = this.#metadata.get(txHashStr);
1192
- if (meta) {
1193
- result.push(meta);
1194
- }
1195
- }
1196
- }
1197
- return result;
1198
- },
1199
- getPendingFeePayers: (): string[] => {
1200
- return Array.from(this.#feePayerToTxHashes.keys());
1201
- },
1202
- getFeePayerPendingTxs: (feePayer: string): TxMetaData[] => {
1203
- return this.#getFeePayerPendingTxs(feePayer);
1204
- },
1205
- getPendingTxCount: (): number => {
1206
- return this.getPendingTxCount();
1207
- },
1208
- getLowestPriorityPending: (limit: number): string[] => {
1209
- return this.getLowestPriorityPending(limit).map(h => h.toString());
1210
- },
1211
- deleteTxs: async (txHashes: string[]): Promise<void> => {
1212
- await this.#store.transactionAsync(async () => {
1213
- for (const txHashStr of txHashes) {
1214
- await this.#deleteTx(txHashStr);
1215
- }
1216
- });
1217
- this.#callbacks.onTxsRemoved(txHashes);
1218
- },
1064
+ getPendingTxs: () => this.#indices.getPendingTxs(),
1065
+ getPendingFeePayers: () => this.#indices.getPendingFeePayers(),
1066
+ getFeePayerPendingTxs: (feePayer: string) => this.#indices.getFeePayerPendingTxs(feePayer),
1067
+ getPendingTxCount: () => this.#indices.getPendingTxCount(),
1068
+ getLowestPriorityPending: (limit: number) => this.#indices.getLowestPriorityPending(limit),
1069
+ deleteTxs: (txHashes: string[], reason?: string) => this.#evictTxs(txHashes, reason ?? 'unknown'),
1219
1070
  };
1220
1071
  }
1221
1072
 
1222
- /**
1223
- * Creates a PreAddPoolAccess adapter for use with pre-add eviction rules.
1224
- * All methods work with strings and TxMetaData for efficiency.
1225
- */
1226
1073
  #createPreAddPoolAccess(): PreAddPoolAccess {
1227
1074
  return {
1228
- getMetadata: (txHashStr: string): TxMetaData | undefined => {
1229
- const meta = this.#metadata.get(txHashStr);
1230
- if (!meta || this.#getTxState(meta) !== 'pending') {
1075
+ getMetadata: (txHashStr: string) => {
1076
+ const meta = this.#indices.getMetadata(txHashStr);
1077
+ if (!meta || this.#indices.getTxState(meta) !== 'pending') {
1231
1078
  return undefined;
1232
1079
  }
1233
1080
  return meta;
1234
1081
  },
1235
- getTxHashByNullifier: (nullifier: string): string | undefined => {
1236
- return this.#nullifierToTxHash.get(nullifier);
1237
- },
1238
- getFeePayerBalance: async (feePayer: string): Promise<bigint> => {
1082
+ getTxHashByNullifier: (nullifier: string) => this.#indices.getTxHashByNullifier(nullifier),
1083
+ getFeePayerBalance: async (feePayer: string) => {
1239
1084
  const db = this.#worldStateSynchronizer.getCommitted();
1240
1085
  const publicStateSource = new DatabasePublicStateSource(db);
1241
1086
  const balance = await publicStateSource.storageRead(
@@ -1244,22 +1089,9 @@ export class TxPoolV2Impl {
1244
1089
  );
1245
1090
  return balance.toBigInt();
1246
1091
  },
1247
- getFeePayerPendingTxs: (feePayer: string): TxMetaData[] => {
1248
- return this.#getFeePayerPendingTxs(feePayer);
1249
- },
1250
- getPendingTxCount: (): number => {
1251
- return this.getPendingTxCount();
1252
- },
1253
- getLowestPriorityPendingTx: (): TxMetaData | undefined => {
1254
- // Iterate in ascending order to find the lowest priority
1255
- for (const txHashStr of this.#iteratePendingByPriority('asc')) {
1256
- const meta = this.#metadata.get(txHashStr);
1257
- if (meta) {
1258
- return meta;
1259
- }
1260
- }
1261
- return undefined;
1262
- },
1092
+ getFeePayerPendingTxs: (feePayer: string) => this.#indices.getFeePayerPendingTxs(feePayer),
1093
+ getPendingTxCount: () => this.#indices.getPendingTxCount(),
1094
+ getLowestPriorityPendingTx: () => this.#indices.getLowestPriorityPendingTx(),
1263
1095
  };
1264
1096
  }
1265
1097
  }