@aztec/p2p 0.0.1-commit.43c09e3f → 0.0.1-commit.4ad48494d

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 (230) hide show
  1. package/dest/client/factory.d.ts +5 -5
  2. package/dest/client/factory.d.ts.map +1 -1
  3. package/dest/client/factory.js +44 -10
  4. package/dest/client/interface.d.ts +37 -15
  5. package/dest/client/interface.d.ts.map +1 -1
  6. package/dest/client/p2p_client.d.ts +35 -36
  7. package/dest/client/p2p_client.d.ts.map +1 -1
  8. package/dest/client/p2p_client.js +114 -138
  9. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +1 -1
  10. package/dest/config.d.ts +23 -5
  11. package/dest/config.d.ts.map +1 -1
  12. package/dest/config.js +16 -1
  13. package/dest/index.d.ts +2 -1
  14. package/dest/index.d.ts.map +1 -1
  15. package/dest/index.js +1 -0
  16. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +104 -88
  17. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  18. package/dest/mem_pools/attestation_pool/attestation_pool.js +441 -3
  19. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +2 -2
  20. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  21. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +353 -87
  22. package/dest/mem_pools/attestation_pool/index.d.ts +2 -3
  23. package/dest/mem_pools/attestation_pool/index.d.ts.map +1 -1
  24. package/dest/mem_pools/attestation_pool/index.js +1 -2
  25. package/dest/mem_pools/index.d.ts +3 -2
  26. package/dest/mem_pools/index.d.ts.map +1 -1
  27. package/dest/mem_pools/index.js +1 -1
  28. package/dest/mem_pools/interface.d.ts +5 -5
  29. package/dest/mem_pools/interface.d.ts.map +1 -1
  30. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +102 -0
  31. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -0
  32. package/dest/mem_pools/tx_pool_v2/deleted_pool.js +242 -0
  33. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts +1 -1
  34. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -1
  35. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +3 -0
  36. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.js +3 -1
  37. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +3 -1
  38. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts +1 -1
  39. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts.map +1 -1
  40. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.js +3 -1
  41. package/dest/mem_pools/tx_pool_v2/index.d.ts +2 -1
  42. package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -1
  43. package/dest/mem_pools/tx_pool_v2/index.js +1 -0
  44. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +11 -3
  45. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
  46. package/dest/mem_pools/tx_pool_v2/interfaces.js +2 -1
  47. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +27 -3
  48. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
  49. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +39 -5
  50. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +105 -0
  51. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -0
  52. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +345 -0
  53. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +4 -2
  54. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
  55. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +12 -2
  56. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +4 -2
  57. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
  58. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +259 -520
  59. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +3 -3
  60. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
  61. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +3 -3
  62. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
  63. package/dest/msg_validators/tx_validator/block_header_validator.d.ts +16 -3
  64. package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
  65. package/dest/msg_validators/tx_validator/block_header_validator.js +1 -1
  66. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +13 -3
  67. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
  68. package/dest/msg_validators/tx_validator/double_spend_validator.js +4 -4
  69. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +20 -4
  70. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -1
  71. package/dest/msg_validators/tx_validator/timestamp_validator.js +2 -2
  72. package/dest/services/dummy_service.d.ts +10 -2
  73. package/dest/services/dummy_service.d.ts.map +1 -1
  74. package/dest/services/dummy_service.js +6 -0
  75. package/dest/services/encoding.d.ts +2 -2
  76. package/dest/services/encoding.d.ts.map +1 -1
  77. package/dest/services/encoding.js +2 -2
  78. package/dest/services/gossipsub/index.d.ts +3 -0
  79. package/dest/services/gossipsub/index.d.ts.map +1 -0
  80. package/dest/services/gossipsub/index.js +2 -0
  81. package/dest/services/gossipsub/scoring.d.ts +21 -3
  82. package/dest/services/gossipsub/scoring.d.ts.map +1 -1
  83. package/dest/services/gossipsub/scoring.js +24 -7
  84. package/dest/services/gossipsub/topic_score_params.d.ts +161 -0
  85. package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -0
  86. package/dest/services/gossipsub/topic_score_params.js +324 -0
  87. package/dest/services/libp2p/libp2p_service.d.ts +84 -35
  88. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  89. package/dest/services/libp2p/libp2p_service.js +368 -273
  90. package/dest/services/peer-manager/peer_scoring.d.ts +1 -1
  91. package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
  92. package/dest/services/peer-manager/peer_scoring.js +25 -2
  93. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +4 -4
  94. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
  95. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +8 -8
  96. package/dest/services/reqresp/interface.d.ts +10 -1
  97. package/dest/services/reqresp/interface.d.ts.map +1 -1
  98. package/dest/services/reqresp/interface.js +15 -1
  99. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +7 -5
  100. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
  101. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +16 -11
  102. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +21 -10
  103. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
  104. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +27 -11
  105. package/dest/services/reqresp/protocols/tx.d.ts +7 -1
  106. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
  107. package/dest/services/reqresp/protocols/tx.js +20 -0
  108. package/dest/services/reqresp/reqresp.d.ts +1 -1
  109. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  110. package/dest/services/reqresp/reqresp.js +11 -4
  111. package/dest/services/service.d.ts +35 -1
  112. package/dest/services/service.d.ts.map +1 -1
  113. package/dest/services/tx_collection/config.d.ts +22 -4
  114. package/dest/services/tx_collection/config.d.ts.map +1 -1
  115. package/dest/services/tx_collection/config.js +49 -3
  116. package/dest/services/tx_collection/fast_tx_collection.d.ts +6 -5
  117. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
  118. package/dest/services/tx_collection/fast_tx_collection.js +27 -17
  119. package/dest/services/tx_collection/file_store_tx_collection.d.ts +53 -0
  120. package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -0
  121. package/dest/services/tx_collection/file_store_tx_collection.js +165 -0
  122. package/dest/services/tx_collection/file_store_tx_source.d.ts +28 -0
  123. package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -0
  124. package/dest/services/tx_collection/file_store_tx_source.js +59 -0
  125. package/dest/services/tx_collection/index.d.ts +3 -2
  126. package/dest/services/tx_collection/index.d.ts.map +1 -1
  127. package/dest/services/tx_collection/index.js +1 -0
  128. package/dest/services/tx_collection/proposal_tx_collector.d.ts +12 -12
  129. package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +1 -1
  130. package/dest/services/tx_collection/proposal_tx_collector.js +4 -5
  131. package/dest/services/tx_collection/slow_tx_collection.d.ts +6 -2
  132. package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -1
  133. package/dest/services/tx_collection/slow_tx_collection.js +55 -23
  134. package/dest/services/tx_collection/tx_collection.d.ts +19 -7
  135. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
  136. package/dest/services/tx_collection/tx_collection.js +75 -3
  137. package/dest/services/tx_collection/tx_collection_sink.d.ts +15 -6
  138. package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
  139. package/dest/services/tx_collection/tx_collection_sink.js +13 -7
  140. package/dest/services/tx_file_store/config.d.ts +1 -3
  141. package/dest/services/tx_file_store/config.d.ts.map +1 -1
  142. package/dest/services/tx_file_store/config.js +0 -4
  143. package/dest/services/tx_file_store/tx_file_store.d.ts +4 -3
  144. package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -1
  145. package/dest/services/tx_file_store/tx_file_store.js +8 -5
  146. package/dest/services/tx_provider.d.ts +3 -3
  147. package/dest/services/tx_provider.d.ts.map +1 -1
  148. package/dest/services/tx_provider.js +5 -4
  149. package/dest/test-helpers/make-test-p2p-clients.d.ts +3 -3
  150. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  151. package/dest/test-helpers/mock-pubsub.d.ts +27 -1
  152. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  153. package/dest/test-helpers/mock-pubsub.js +97 -2
  154. package/dest/test-helpers/reqresp-nodes.d.ts +1 -1
  155. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  156. package/dest/test-helpers/reqresp-nodes.js +2 -1
  157. package/dest/test-helpers/testbench-utils.d.ts +40 -38
  158. package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
  159. package/dest/test-helpers/testbench-utils.js +128 -59
  160. package/dest/testbench/p2p_client_testbench_worker.js +2 -2
  161. package/package.json +14 -14
  162. package/src/client/factory.ts +81 -13
  163. package/src/client/interface.ts +45 -14
  164. package/src/client/p2p_client.ts +151 -161
  165. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +1 -1
  166. package/src/config.ts +34 -2
  167. package/src/index.ts +1 -0
  168. package/src/mem_pools/attestation_pool/attestation_pool.ts +496 -91
  169. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +442 -102
  170. package/src/mem_pools/attestation_pool/index.ts +9 -2
  171. package/src/mem_pools/index.ts +4 -1
  172. package/src/mem_pools/interface.ts +4 -4
  173. package/src/mem_pools/tx_pool_v2/README.md +103 -16
  174. package/src/mem_pools/tx_pool_v2/deleted_pool.ts +310 -0
  175. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +3 -0
  176. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.ts +1 -1
  177. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +1 -1
  178. package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +3 -1
  179. package/src/mem_pools/tx_pool_v2/index.ts +1 -0
  180. package/src/mem_pools/tx_pool_v2/interfaces.ts +10 -2
  181. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +57 -6
  182. package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +433 -0
  183. package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +10 -1
  184. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +266 -607
  185. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +2 -2
  186. package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +2 -2
  187. package/src/msg_validators/tx_validator/block_header_validator.ts +15 -3
  188. package/src/msg_validators/tx_validator/double_spend_validator.ts +11 -6
  189. package/src/msg_validators/tx_validator/timestamp_validator.ts +19 -14
  190. package/src/services/dummy_service.ts +12 -0
  191. package/src/services/encoding.ts +2 -2
  192. package/src/services/gossipsub/README.md +626 -0
  193. package/src/services/gossipsub/index.ts +2 -0
  194. package/src/services/gossipsub/scoring.ts +29 -5
  195. package/src/services/gossipsub/topic_score_params.ts +451 -0
  196. package/src/services/libp2p/libp2p_service.ts +370 -275
  197. package/src/services/peer-manager/peer_scoring.ts +25 -0
  198. package/src/services/reqresp/batch-tx-requester/README.md +7 -7
  199. package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +11 -11
  200. package/src/services/reqresp/interface.ts +26 -1
  201. package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +23 -14
  202. package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +38 -15
  203. package/src/services/reqresp/protocols/tx.ts +22 -0
  204. package/src/services/reqresp/reqresp.ts +13 -3
  205. package/src/services/service.ts +40 -0
  206. package/src/services/tx_collection/config.ts +74 -6
  207. package/src/services/tx_collection/fast_tx_collection.ts +28 -26
  208. package/src/services/tx_collection/file_store_tx_collection.ts +198 -0
  209. package/src/services/tx_collection/file_store_tx_source.ts +73 -0
  210. package/src/services/tx_collection/index.ts +2 -1
  211. package/src/services/tx_collection/proposal_tx_collector.ts +12 -14
  212. package/src/services/tx_collection/slow_tx_collection.ts +64 -30
  213. package/src/services/tx_collection/tx_collection.ts +109 -13
  214. package/src/services/tx_collection/tx_collection_sink.ts +17 -7
  215. package/src/services/tx_file_store/config.ts +0 -6
  216. package/src/services/tx_file_store/tx_file_store.ts +9 -7
  217. package/src/services/tx_provider.ts +8 -7
  218. package/src/test-helpers/make-test-p2p-clients.ts +3 -3
  219. package/src/test-helpers/mock-pubsub.ts +133 -3
  220. package/src/test-helpers/reqresp-nodes.ts +2 -1
  221. package/src/test-helpers/testbench-utils.ts +127 -71
  222. package/src/testbench/p2p_client_testbench_worker.ts +2 -2
  223. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +0 -40
  224. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +0 -1
  225. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +0 -218
  226. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +0 -31
  227. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +0 -1
  228. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +0 -180
  229. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +0 -320
  230. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +0 -264
@@ -1,12 +1,15 @@
1
+ import { BlockNumber } from '@aztec/foundation/branded-types';
1
2
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
2
3
  import { computeFeePayerBalanceStorageSlot } from '@aztec/protocol-contracts/fee-juice';
3
4
  import { AztecAddress } from '@aztec/stdlib/aztec-address';
4
5
  import { DatabasePublicStateSource } from '@aztec/stdlib/trees';
5
6
  import { Tx, TxHash } from '@aztec/stdlib/tx';
6
7
  import { TxArchive } from './archive/index.js';
8
+ import { DeletedPool } from './deleted_pool.js';
7
9
  import { EvictionManager, FeePayerBalanceEvictionRule, FeePayerBalancePreAddRule, InvalidTxsAfterMiningRule, InvalidTxsAfterReorgRule, LowPriorityEvictionRule, LowPriorityPreAddRule, NullifierConflictRule } from './eviction/index.js';
8
10
  import { DEFAULT_TX_POOL_V2_CONFIG } from './interfaces.js';
9
- import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } from './tx_metadata.js';
11
+ import { buildTxMetaData, checkNullifierConflict } from './tx_metadata.js';
12
+ import { TxPoolIndices } from './tx_pool_indices.js';
10
13
  /**
11
14
  * Implementation of TxPoolV2 logic.
12
15
  *
@@ -18,33 +21,30 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
18
21
  // === Dependencies ===
19
22
  #l2BlockSource;
20
23
  #worldStateSynchronizer;
21
- #pendingTxValidator;
24
+ #createTxValidator;
22
25
  // === In-Memory Indices ===
23
- /** Primary metadata store: txHash -> TxMetaData */ #metadata = new Map();
24
- /** Nullifier to txHash index (pending txs only) */ #nullifierToTxHash = new Map();
25
- /** Fee payer to txHashes index (pending txs only) */ #feePayerToTxHashes = new Map();
26
- /**
27
- * Pending txHashes grouped by priority fee.
28
- * Outer map: priorityFee -> Set of txHashes at that fee level.
29
- */ #pendingByPriority = new Map();
30
- /** Protected transactions: txHash -> slotNumber. Includes txs we have and txs we expect to receive. */ #protectedTransactions = new Map();
26
+ #indices = new TxPoolIndices();
31
27
  // === Config & Services ===
32
28
  #config;
33
29
  #archive;
30
+ #deletedPool;
34
31
  #evictionManager;
32
+ #dateProvider;
35
33
  #log;
36
34
  #callbacks;
37
- constructor(store, archiveStore, deps, callbacks, config = {}, log){
35
+ constructor(store, archiveStore, deps, callbacks, config = {}, dateProvider, log){
38
36
  this.#store = store;
39
37
  this.#txsDB = store.openMap('txs');
40
38
  this.#l2BlockSource = deps.l2BlockSource;
41
39
  this.#worldStateSynchronizer = deps.worldStateSynchronizer;
42
- this.#pendingTxValidator = deps.pendingTxValidator;
40
+ this.#createTxValidator = deps.createTxValidator;
43
41
  this.#config = {
44
42
  ...DEFAULT_TX_POOL_V2_CONFIG,
45
43
  ...config
46
44
  };
47
45
  this.#archive = new TxArchive(archiveStore, this.#config.archivedTxLimit, log);
46
+ this.#deletedPool = new DeletedPool(store, this.#txsDB, log);
47
+ this.#dateProvider = dateProvider;
48
48
  this.#log = log;
49
49
  this.#callbacks = callbacks;
50
50
  // Setup eviction manager with rules
@@ -75,20 +75,32 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
75
75
  * Note: Protected status is lost on restart. All non-mined txs are rebuilt as pending
76
76
  * by running pre-add rules to resolve nullifier conflicts, balance checks, and pool size limits.
77
77
  */ async hydrateFromDatabase() {
78
- // Step 1: Load all transactions from DB
78
+ // Step 0: Hydrate deleted pool state
79
+ await this.#deletedPool.hydrateFromDatabase();
80
+ // Step 1: Load all transactions from DB (excluding soft-deleted)
79
81
  const { loaded, errors: deserializationErrors } = await this.#loadAllTxsFromDb();
80
82
  // Step 2: Check mined status for each tx
81
83
  await this.#markMinedStatusBatch(loaded.map((l)=>l.meta));
82
84
  // Step 3: Partition by mined status
83
- const { mined, nonMined } = this.#partitionByMinedStatus(loaded);
85
+ const mined = [];
86
+ const nonMined = [];
87
+ for (const entry of loaded){
88
+ if (entry.meta.minedL2BlockId !== undefined) {
89
+ mined.push(entry.meta);
90
+ } else {
91
+ nonMined.push(entry);
92
+ }
93
+ }
84
94
  // Step 4: Validate non-mined transactions
85
- const { valid, invalid } = await this.#validateNonMinedTxs(nonMined);
95
+ const { valid, invalid } = await this.#revalidateMetadata(nonMined.map((e)=>e.meta), 'on startup');
86
96
  // Step 5: Populate mined indices (these don't need conflict resolution)
87
- this.#populateMinedIndices(mined);
97
+ for (const meta of mined){
98
+ this.#indices.addMined(meta);
99
+ }
88
100
  // Step 6: Rebuild pending pool by running pre-add rules for each tx
89
101
  // This resolves nullifier conflicts, fee payer balance issues, and pool size limits
90
102
  const { rejected } = await this.#rebuildPendingPool(valid);
91
- // Step 7: Delete invalid and rejected txs from DB
103
+ // Step 7: Delete invalid and rejected txs from DB only (indices were never populated for these)
92
104
  const toDelete = [
93
105
  ...deserializationErrors,
94
106
  ...invalid,
@@ -102,13 +114,14 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
102
114
  await this.#txsDB.delete(txHashStr);
103
115
  }
104
116
  });
105
- this.#log.info(`Deleted ${toDelete.length} invalid/rejected transactions on startup`);
117
+ this.#log.info(`Deleted ${toDelete.length} invalid/rejected transactions on startup`, {
118
+ txHashes: toDelete
119
+ });
106
120
  }
107
121
  async addPendingTxs(txs, opts) {
108
122
  const accepted = [];
109
123
  const ignored = [];
110
124
  const rejected = [];
111
- const newlyAdded = [];
112
125
  const acceptedPending = new Set();
113
126
  const poolAccess = this.#createPreAddPoolAccess();
114
127
  await this.#store.transactionAsync(async ()=>{
@@ -116,29 +129,30 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
116
129
  const txHash = tx.getTxHash();
117
130
  const txHashStr = txHash.toString();
118
131
  // Skip duplicates
119
- if (this.#isDuplicateTx(txHashStr)) {
132
+ if (this.#indices.has(txHashStr)) {
120
133
  ignored.push(txHash);
121
134
  continue;
122
135
  }
123
136
  // Check mined status first (applies to all paths)
124
137
  const minedBlockId = await this.#getMinedBlockId(txHash);
125
- const preProtectedSlot = this.#protectedTransactions.get(txHashStr);
138
+ const preProtectedSlot = this.#indices.getProtectionSlot(txHashStr);
126
139
  if (minedBlockId) {
127
140
  // Already mined - add directly (protection already set if pre-protected)
128
- await this.#addNewMinedTx(tx, minedBlockId);
141
+ await this.#addTx(tx, {
142
+ mined: minedBlockId
143
+ }, opts);
129
144
  accepted.push(txHash);
130
- newlyAdded.push(tx);
131
145
  } else if (preProtectedSlot !== undefined) {
132
146
  // Pre-protected and not mined - add as protected (bypass validation)
133
- await this.#addNewProtectedTx(tx, preProtectedSlot);
147
+ await this.#addTx(tx, {
148
+ protected: preProtectedSlot
149
+ }, opts);
134
150
  accepted.push(txHash);
135
- newlyAdded.push(tx);
136
151
  } else {
137
152
  // Regular pending tx - validate and run pre-add rules
138
- const result = await this.#tryAddRegularPendingTx(tx, poolAccess, acceptedPending, ignored);
153
+ const result = await this.#tryAddRegularPendingTx(tx, opts, poolAccess, acceptedPending, ignored);
139
154
  if (result.status === 'accepted') {
140
155
  acceptedPending.add(txHashStr);
141
- newlyAdded.push(tx);
142
156
  } else if (result.status === 'rejected') {
143
157
  rejected.push(txHash);
144
158
  } else {
@@ -153,35 +167,29 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
153
167
  }
154
168
  // Run post-add eviction rules for pending txs
155
169
  if (acceptedPending.size > 0) {
156
- const feePayers = Array.from(acceptedPending).map((txHash)=>this.#metadata.get(txHash).feePayer);
170
+ const feePayers = Array.from(acceptedPending).map((txHash)=>this.#indices.getMetadata(txHash).feePayer);
157
171
  const uniqueFeePayers = new Set(feePayers);
158
172
  await this.#evictionManager.evictAfterNewTxs(Array.from(acceptedPending), [
159
173
  ...uniqueFeePayers
160
174
  ]);
161
175
  }
162
- // Emit events
163
- if (newlyAdded.length > 0) {
164
- this.#callbacks.onTxsAdded(newlyAdded, opts);
165
- }
166
176
  return {
167
177
  accepted,
168
178
  ignored,
169
179
  rejected
170
180
  };
171
181
  }
172
- /** Validates and adds a regular pending tx. Returns status. */ async #tryAddRegularPendingTx(tx, poolAccess, acceptedPending, ignored) {
182
+ /** Validates and adds a regular pending tx. Returns status. */ async #tryAddRegularPendingTx(tx, opts, poolAccess, acceptedPending, ignored) {
173
183
  const txHash = tx.getTxHash();
174
184
  const txHashStr = txHash.toString();
175
- // Validate transaction
176
- const validationResult = await this.#pendingTxValidator.validateTx(tx);
177
- if (validationResult.result !== 'valid') {
178
- this.#log.info(`Rejecting tx ${txHashStr}: ${validationResult.reason?.join(', ')}`);
185
+ // Build metadata and validate using metadata
186
+ const meta = await buildTxMetaData(tx);
187
+ if (!await this.#validateMeta(meta)) {
179
188
  return {
180
189
  status: 'rejected'
181
190
  };
182
191
  }
183
- // Build metadata and run pre-add rules
184
- const meta = await buildTxMetaData(tx);
192
+ // Run pre-add rules
185
193
  const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
186
194
  if (preAddResult.shouldIgnore) {
187
195
  this.#log.debug(`Ignoring tx ${txHashStr}: ${preAddResult.reason}`);
@@ -189,17 +197,21 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
189
197
  status: 'ignored'
190
198
  };
191
199
  }
192
- // Evict conflicts (tracking intra-batch evictions)
200
+ // Evict conflicts
193
201
  for (const evictHashStr of preAddResult.txHashesToEvict){
194
202
  await this.#deleteTx(evictHashStr);
195
- this.#log.debug(`Evicted tx ${evictHashStr} due to higher-fee tx ${txHashStr}`);
203
+ this.#log.debug(`Evicted tx ${evictHashStr} due to higher-fee tx ${txHashStr}`, {
204
+ evictedTxHash: evictHashStr,
205
+ replacementTxHash: txHashStr
206
+ });
196
207
  if (acceptedPending.has(evictHashStr)) {
208
+ // Evicted tx was from this batch - mark as ignored in result
197
209
  acceptedPending.delete(evictHashStr);
198
210
  ignored.push(TxHash.fromString(evictHashStr));
199
211
  }
200
212
  }
201
213
  // Add the transaction
202
- await this.#addNewPendingTx(tx);
214
+ await this.#addTx(tx, 'pending', opts);
203
215
  return {
204
216
  status: 'accepted'
205
217
  };
@@ -207,62 +219,62 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
207
219
  async canAddPendingTx(tx) {
208
220
  const txHashStr = tx.getTxHash().toString();
209
221
  // Check if already in pool
210
- if (this.#metadata.has(txHashStr)) {
222
+ if (this.#indices.has(txHashStr)) {
211
223
  return 'ignored';
212
224
  }
213
- // Validate transaction
214
- const validationResult = await this.#pendingTxValidator.validateTx(tx);
215
- if (validationResult.result !== 'valid') {
225
+ // Build metadata and validate using metadata
226
+ const meta = await buildTxMetaData(tx);
227
+ const validationResult = await this.#validateMeta(meta, undefined, 'can add pending');
228
+ if (validationResult !== true) {
216
229
  return 'rejected';
217
230
  }
218
- // Build metadata and use pre-add rules
219
- const meta = await buildTxMetaData(tx);
231
+ // Use pre-add rules
220
232
  const poolAccess = this.#createPreAddPoolAccess();
221
233
  const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
222
234
  return preAddResult.shouldIgnore ? 'ignored' : 'accepted';
223
235
  }
224
236
  async addProtectedTxs(txs, block, opts) {
225
237
  const slotNumber = block.globalVariables.slotNumber;
226
- const newlyAdded = [];
227
238
  await this.#store.transactionAsync(async ()=>{
228
239
  for (const tx of txs){
229
240
  const txHash = tx.getTxHash();
230
241
  const txHashStr = txHash.toString();
231
- const isNew = !this.#metadata.has(txHashStr);
242
+ const isNew = !this.#indices.has(txHashStr);
232
243
  const minedBlockId = await this.#getMinedBlockId(txHash);
233
244
  if (isNew) {
234
- // New tx - add as mined or protected
245
+ // New tx - add as mined or protected (callback emitted by #addTx)
235
246
  if (minedBlockId) {
236
- await this.#addNewMinedTx(tx, minedBlockId);
237
- this.#protectedTransactions.set(txHashStr, slotNumber);
247
+ await this.#addTx(tx, {
248
+ mined: minedBlockId
249
+ }, opts);
250
+ this.#indices.setProtection(txHashStr, slotNumber);
238
251
  } else {
239
- await this.#addNewProtectedTx(tx, slotNumber);
252
+ await this.#addTx(tx, {
253
+ protected: slotNumber
254
+ }, opts);
240
255
  }
241
- newlyAdded.push(tx);
242
256
  } else {
243
257
  // Existing tx - update protection and mined status
244
- this.#updateProtection(txHashStr, slotNumber);
258
+ this.#indices.updateProtection(txHashStr, slotNumber);
245
259
  if (minedBlockId) {
246
- this.#markAsMined(this.#metadata.get(txHashStr), minedBlockId);
260
+ const meta = this.#indices.getMetadata(txHashStr);
261
+ this.#indices.markAsMined(meta, minedBlockId);
247
262
  }
248
263
  }
249
264
  }
250
265
  });
251
- if (newlyAdded.length > 0) {
252
- this.#callbacks.onTxsAdded(newlyAdded, opts);
253
- }
254
266
  }
255
267
  protectTxs(txHashes, block) {
256
268
  const slotNumber = block.globalVariables.slotNumber;
257
269
  const missing = [];
258
270
  for (const txHash of txHashes){
259
271
  const txHashStr = txHash.toString();
260
- if (this.#metadata.has(txHashStr)) {
261
- // Step 1a: Update protection for existing tx
262
- this.#updateProtection(txHashStr, slotNumber);
272
+ if (this.#indices.has(txHashStr)) {
273
+ // Update protection for existing tx
274
+ this.#indices.updateProtection(txHashStr, slotNumber);
263
275
  } else {
264
- // Step 1b: Pre-record protection for tx we don't have yet
265
- this.#protectedTransactions.set(txHashStr, slotNumber);
276
+ // Pre-record protection for tx we don't have yet
277
+ this.#indices.setProtection(txHashStr, slotNumber);
266
278
  missing.push(txHash);
267
279
  }
268
280
  }
@@ -271,25 +283,22 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
271
283
  async addMinedTxs(txs, block, opts) {
272
284
  // Step 1: Build block ID
273
285
  const blockId = await this.#buildBlockId(block);
274
- const newlyAdded = [];
275
286
  await this.#store.transactionAsync(async ()=>{
276
287
  for (const tx of txs){
277
288
  const txHashStr = tx.getTxHash().toString();
278
- const existingMeta = this.#metadata.get(txHashStr);
289
+ const existingMeta = this.#indices.getMetadata(txHashStr);
279
290
  if (existingMeta) {
280
- // Step 2a: Mark existing tx as mined
281
- this.#markAsMined(existingMeta, blockId);
291
+ // Mark existing tx as mined
292
+ this.#indices.markAsMined(existingMeta, blockId);
282
293
  } else {
283
- // Step 2b: Add new mined tx
284
- await this.#addNewMinedTx(tx, blockId);
285
- newlyAdded.push(tx);
294
+ // Add new mined tx (callback emitted by #addTx)
295
+ await this.#addTx(tx, {
296
+ mined: blockId
297
+ }, opts);
286
298
  }
299
+ await this.#deletedPool.clearIfMinedHigher(txHashStr, blockId.number);
287
300
  }
288
301
  });
289
- // Step 3: Emit events for newly added txs
290
- if (newlyAdded.length > 0) {
291
- this.#callbacks.onTxsAdded(newlyAdded, opts);
292
- }
293
302
  }
294
303
  async handleMinedBlock(block) {
295
304
  // Step 1: Build block ID
@@ -301,32 +310,36 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
301
310
  const feePayers = [];
302
311
  const found = [];
303
312
  for (const txHash of txHashes){
304
- const meta = this.#metadata.get(txHash.toString());
313
+ const meta = this.#indices.getMetadata(txHash.toString());
305
314
  if (meta) {
306
315
  feePayers.push(meta.feePayer);
307
316
  found.push(meta);
308
317
  }
309
318
  }
310
319
  // Step 4: Mark txs as mined (only those we have in the pool)
311
- this.#markTxsAsMined(found, blockId);
320
+ for (const meta of found){
321
+ this.#indices.markAsMined(meta, blockId);
322
+ await this.#deletedPool.clearIfMinedHigher(meta.txHash, blockId.number);
323
+ }
312
324
  // Step 5: Run eviction rules (remove pending txs with conflicting nullifiers/expired timestamps)
313
325
  await this.#evictionManager.evictAfterNewBlock(block.header, nullifiers, feePayers);
314
- this.#callbacks.onTxsRemoved(txHashes.map((h)=>h.toBigInt()));
315
326
  this.#log.info(`Marked ${found.length} txs as mined in block ${blockId.number}`);
316
327
  }
317
328
  async prepareForSlot(slotNumber) {
329
+ // Step 0: Clean up slot-deleted txs from previous slots
330
+ await this.#deletedPool.cleanupSlotDeleted(slotNumber);
318
331
  // Step 1: Find expired protected txs
319
- const expiredProtected = this.#findExpiredProtectedTxs(slotNumber);
332
+ const expiredProtected = this.#indices.findExpiredProtectedTxs(slotNumber);
320
333
  // Step 2: Clear protection for all expired entries (including those without metadata)
321
- this.#clearProtection(expiredProtected);
334
+ this.#indices.clearProtection(expiredProtected);
322
335
  // Step 3: Filter to only txs that have metadata and are not mined
323
- const txsToRestore = this.#filterRestorable(expiredProtected);
336
+ const txsToRestore = this.#indices.filterRestorable(expiredProtected);
324
337
  if (txsToRestore.length === 0) {
325
338
  return;
326
339
  }
327
340
  this.#log.info(`Preparing for slot ${slotNumber}: unprotecting ${txsToRestore.length} txs`);
328
341
  // Step 4: Validate for pending pool
329
- const { valid, invalid } = await this.#validateForPending(txsToRestore);
342
+ const { valid, invalid } = await this.#revalidateMetadata(txsToRestore, 'during prepareForSlot');
330
343
  // Step 5: Resolve nullifier conflicts and add winners to pending indices
331
344
  const { added, toEvict } = this.#applyNullifierConflictResolution(valid);
332
345
  // Step 6: Delete invalid and evicted txs
@@ -345,58 +358,77 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
345
358
  }
346
359
  async handlePrunedBlocks(latestBlock) {
347
360
  // Step 1: Find transactions mined after the prune point
348
- const txsToUnmine = this.#findTxsMinedAfter(latestBlock.number);
361
+ const txsToUnmine = this.#indices.findTxsMinedAfter(latestBlock.number);
349
362
  if (txsToUnmine.length === 0) {
350
363
  this.#log.debug(`No transactions to un-mine for prune to block ${latestBlock.number}`);
351
364
  return;
352
365
  }
353
366
  this.#log.info(`Handling prune to block ${latestBlock.number}: un-mining ${txsToUnmine.length} txs`);
354
- // Step 2: Unmine - clear mined status from metadata
355
- this.#unmineTxs(txsToUnmine);
356
- // Step 3: Filter out protected txs (they'll be handled by prepareForSlot)
357
- const unprotectedTxs = this.#filterUnprotected(txsToUnmine);
367
+ // Step 2: Mark ALL un-mined txs with their original mined block number
368
+ // This ensures they get soft-deleted if removed later, and only hard-deleted
369
+ // when their original mined block is finalized
370
+ await this.#deletedPool.markFromPrunedBlock(txsToUnmine.map((m)=>({
371
+ txHash: m.txHash,
372
+ minedAtBlock: BlockNumber(m.minedL2BlockId.number)
373
+ })));
374
+ // Step 3: Unmine - clear mined status from metadata
375
+ for (const meta of txsToUnmine){
376
+ this.#indices.markAsUnmined(meta);
377
+ }
378
+ // Step 4: Filter out protected txs (they'll be handled by prepareForSlot)
379
+ const unprotectedTxs = this.#indices.filterUnprotected(txsToUnmine);
358
380
  // Step 4: Validate for pending pool
359
- const { valid, invalid } = await this.#validateForPending(unprotectedTxs);
360
- // Step 5: Resolve nullifier conflicts and add winners to pending indices
381
+ const { valid, invalid } = await this.#revalidateMetadata(unprotectedTxs, 'during handlePrunedBlocks');
382
+ // Step 6: Resolve nullifier conflicts and add winners to pending indices
361
383
  const { toEvict } = this.#applyNullifierConflictResolution(valid);
362
- // Step 6: Delete invalid and evicted txs
384
+ // Step 7: Delete invalid and evicted txs
363
385
  await this.#deleteTxsBatch([
364
386
  ...invalid,
365
387
  ...toEvict
366
388
  ]);
367
- // Step 7: Run eviction rules for ALL pending txs (not just restored ones)
389
+ this.#log.info(`Handled prune to block ${latestBlock.number}: ${valid.length} txs restored to pending, ${invalid.length} invalid, ${toEvict.length} evicted due to nullifier conflicts`, {
390
+ txHashesRestored: valid.map((m)=>m.txHash),
391
+ txHashesInvalid: invalid,
392
+ txHashesEvicted: toEvict
393
+ });
394
+ // Step 8: Run eviction rules for ALL pending txs (not just restored ones)
368
395
  // This handles cases like existing pending txs with invalid fee payer balances
369
396
  await this.#evictionManager.evictAfterChainPrune(latestBlock.number);
370
397
  }
371
398
  async handleFailedExecution(txHashes) {
372
- // Step 1: Delete failed txs
399
+ // Delete failed txs
373
400
  await this.#deleteTxsBatch(txHashes.map((h)=>h.toString()));
374
- this.#log.info(`Deleted ${txHashes.length} failed txs`);
401
+ this.#log.info(`Deleted ${txHashes.length} failed txs`, {
402
+ txHashes: txHashes.map((h)=>h.toString())
403
+ });
375
404
  }
376
405
  async handleFinalizedBlock(block) {
377
406
  const blockNumber = block.globalVariables.blockNumber;
378
- // Step 1: Find txs mined at or before finalized block
379
- const txsToFinalize = this.#findTxsMinedAtOrBefore(blockNumber);
380
- if (txsToFinalize.length === 0) {
381
- return;
382
- }
383
- // Step 2: Collect txs for archiving (before deletion)
407
+ // Step 1: Find mined txs at or before finalized block
408
+ const minedTxsToFinalize = this.#indices.findTxsMinedAtOrBefore(blockNumber);
409
+ // Step 2: Collect mined txs for archiving (before deletion)
384
410
  const txsToArchive = [];
385
411
  if (this.#archive.isEnabled()) {
386
- for (const txHashStr of txsToFinalize){
412
+ for (const txHashStr of minedTxsToFinalize){
387
413
  const buffer = await this.#txsDB.getAsync(txHashStr);
388
414
  if (buffer) {
389
415
  txsToArchive.push(Tx.fromBuffer(buffer));
390
416
  }
391
417
  }
392
418
  }
393
- // Step 3: Delete from active pool
394
- await this.#deleteTxsBatch(txsToFinalize);
395
- // Step 4: Archive
419
+ // Step 3: Delete mined txs from active pool
420
+ await this.#deleteTxsBatch(minedTxsToFinalize);
421
+ // Step 4: Finalize soft-deleted txs
422
+ await this.#deletedPool.finalizeBlock(blockNumber);
423
+ // Step 5: Archive mined txs
396
424
  if (txsToArchive.length > 0) {
397
425
  await this.#archive.archiveTxs(txsToArchive);
398
426
  }
399
- this.#log.info(`Finalized ${txsToFinalize.length} txs from blocks up to ${blockNumber}`);
427
+ if (minedTxsToFinalize.length > 0) {
428
+ this.#log.info(`Finalized ${minedTxsToFinalize.length} mined txs from blocks up to ${blockNumber}`, {
429
+ txHashes: minedTxsToFinalize
430
+ });
431
+ }
400
432
  }
401
433
  // === Query Methods ===
402
434
  async getTxByHash(txHash) {
@@ -412,42 +444,46 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
412
444
  return results;
413
445
  }
414
446
  hasTxs(txHashes) {
415
- return txHashes.map((h)=>this.#metadata.has(h.toString()));
447
+ return txHashes.map((h)=>{
448
+ const hashStr = h.toString();
449
+ return this.#indices.has(hashStr) || this.#deletedPool.isSoftDeleted(hashStr);
450
+ });
416
451
  }
417
452
  getTxStatus(txHash) {
418
- const meta = this.#metadata.get(txHash.toString());
419
- if (!meta) {
420
- return undefined;
453
+ const txHashStr = txHash.toString();
454
+ const meta = this.#indices.getMetadata(txHashStr);
455
+ if (meta) {
456
+ return this.#indices.getTxState(meta);
421
457
  }
422
- return this.#getTxState(meta);
458
+ // Check if soft-deleted
459
+ if (this.#deletedPool.isSoftDeleted(txHashStr)) {
460
+ return 'deleted';
461
+ }
462
+ return undefined;
423
463
  }
424
464
  getPendingTxHashes() {
425
465
  return [
426
- ...this.#iteratePendingByPriority('desc')
466
+ ...this.#indices.iteratePendingByPriority('desc')
467
+ ].map((hash)=>TxHash.fromString(hash));
468
+ }
469
+ getEligiblePendingTxHashes() {
470
+ const maxReceivedAt = this.#dateProvider.now() - this.#config.minTxPoolAgeMs;
471
+ return [
472
+ ...this.#indices.iterateEligiblePendingByPriority('desc', maxReceivedAt)
427
473
  ].map((hash)=>TxHash.fromString(hash));
428
474
  }
429
475
  getPendingTxCount() {
430
- let count = 0;
431
- for (const hashes of this.#pendingByPriority.values()){
432
- count += hashes.size;
433
- }
434
- return count;
476
+ return this.#indices.getPendingTxCount();
435
477
  }
436
478
  getMinedTxHashes() {
437
- const result = [];
438
- for (const [txHash, meta] of this.#metadata){
439
- if (meta.minedL2BlockId !== undefined) {
440
- result.push([
441
- TxHash.fromString(txHash),
442
- meta.minedL2BlockId
443
- ]);
444
- }
445
- }
446
- return result;
479
+ return this.#indices.getMinedTxs().map(([hash, blockId])=>[
480
+ TxHash.fromString(hash),
481
+ blockId
482
+ ]);
447
483
  }
448
484
  getMinedTxCount() {
449
485
  let count = 0;
450
- for (const meta of this.#metadata.values()){
486
+ for (const [, meta] of this.#indices.iterateMetadata()){
451
487
  if (meta.minedL2BlockId !== undefined) {
452
488
  count++;
453
489
  }
@@ -455,26 +491,16 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
455
491
  return count;
456
492
  }
457
493
  isEmpty() {
458
- return this.#metadata.size === 0;
494
+ return this.#indices.isEmpty();
459
495
  }
460
496
  getTxCount() {
461
- return this.#metadata.size;
497
+ return this.#indices.getTxCount();
462
498
  }
463
499
  getArchivedTxByHash(txHash) {
464
500
  return this.#archive.getTxByHash(txHash);
465
501
  }
466
502
  getLowestPriorityPending(limit) {
467
- if (limit <= 0) {
468
- return [];
469
- }
470
- const result = [];
471
- for (const hash of this.#iteratePendingByPriority('asc')){
472
- result.push(TxHash.fromString(hash));
473
- if (result.length >= limit) {
474
- break;
475
- }
476
- }
477
- return result;
503
+ return this.#indices.getLowestPriorityPending(limit).map((h)=>TxHash.fromString(h));
478
504
  }
479
505
  // === Configuration ===
480
506
  updateConfig(config) {
@@ -485,138 +511,95 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
485
511
  this.#config.archivedTxLimit = config.archivedTxLimit;
486
512
  this.#archive.updateLimit(config.archivedTxLimit);
487
513
  }
514
+ if (config.minTxPoolAgeMs !== undefined) {
515
+ this.#config.minTxPoolAgeMs = config.minTxPoolAgeMs;
516
+ }
488
517
  // Update eviction rules with new config
489
518
  this.#evictionManager.updateConfig(config);
490
519
  }
491
520
  // === Pool Read Access ===
492
521
  getPoolReadAccess() {
493
522
  return {
494
- getMetadata: (txHash)=>this.#metadata.get(txHash),
495
- getTxHashByNullifier: (nullifier)=>this.#nullifierToTxHash.get(nullifier),
496
- getTxHashesByFeePayer: (feePayer)=>this.#feePayerToTxHashes.get(feePayer),
497
- getPendingTxCount: ()=>this.getPendingTxCount()
523
+ getMetadata: (txHash)=>this.#indices.getMetadata(txHash),
524
+ getTxHashByNullifier: (nullifier)=>this.#indices.getTxHashByNullifier(nullifier),
525
+ getTxHashesByFeePayer: (feePayer)=>this.#indices.getTxHashesByFeePayer(feePayer),
526
+ getPendingTxCount: ()=>this.#indices.getPendingTxCount()
498
527
  };
499
528
  }
500
529
  // === Metrics ===
501
530
  countTxs() {
502
- let pending = 0;
503
- let protected_ = 0;
504
- let mined = 0;
505
- for (const meta of this.#metadata.values()){
506
- const state = this.#getTxState(meta);
507
- if (state === 'pending') {
508
- pending++;
509
- } else if (state === 'protected') {
510
- protected_++;
511
- } else if (state === 'mined') {
512
- mined++;
513
- }
514
- }
515
- return {
516
- pending,
517
- protected: protected_,
518
- mined
519
- };
531
+ return this.#indices.countTxs();
520
532
  }
521
533
  // ============================================================================
522
- // PRIVATE QUERY IMPLEMENTATIONS
534
+ // PRIVATE HELPERS - Transaction Management
523
535
  // ============================================================================
524
536
  /**
525
- * Derives the transaction state from its metadata and protection status.
526
- * A transaction is:
527
- * - 'mined' if it has a minedL2BlockId
528
- * - 'protected' if it's in the protectedTransactions map (but not mined)
529
- * - 'pending' otherwise
530
- */ #getTxState(meta) {
531
- if (meta.minedL2BlockId !== undefined) {
532
- return 'mined';
533
- } else if (this.#protectedTransactions.has(meta.txHash)) {
534
- return 'protected';
537
+ * Adds a new transaction to the pool with the specified state.
538
+ * Emits onTxsAdded callback immediately after DB write.
539
+ */ async #addTx(tx, state, opts = {}) {
540
+ const txHashStr = tx.getTxHash().toString();
541
+ const meta = await buildTxMetaData(tx);
542
+ meta.receivedAt = this.#dateProvider.now();
543
+ await this.#txsDB.set(txHashStr, tx.toBuffer());
544
+ await this.#deletedPool.clearSoftDeleted(txHashStr);
545
+ this.#callbacks.onTxsAdded([
546
+ tx
547
+ ], opts);
548
+ if (state === 'pending') {
549
+ this.#indices.addPending(meta);
550
+ } else if ('protected' in state) {
551
+ this.#indices.addProtected(meta, state.protected);
535
552
  } else {
536
- return 'pending';
553
+ meta.minedL2BlockId = state.mined;
554
+ this.#indices.addMined(meta);
537
555
  }
556
+ const stateStr = typeof state === 'string' ? state : Object.keys(state)[0];
557
+ this.#log.verbose(`Added ${stateStr} tx ${txHashStr}`, {
558
+ eventName: 'tx-added-to-pool',
559
+ state: stateStr
560
+ });
561
+ return meta;
538
562
  }
539
563
  /**
540
- * Iterates pending transaction hashes in priority order.
541
- * @param order - 'desc' for highest priority first, 'asc' for lowest priority first
542
- */ *#iteratePendingByPriority(order) {
543
- // Use shared comparators, negating for descending order
544
- const feeCompareFn = order === 'desc' ? (a, b)=>compareFee(b, a) : (a, b)=>compareFee(a, b);
545
- const hashCompareFn = order === 'desc' ? (a, b)=>compareTxHash(b, a) : (a, b)=>compareTxHash(a, b);
546
- const sortedFees = [
547
- ...this.#pendingByPriority.keys()
548
- ].sort(feeCompareFn);
549
- for (const fee of sortedFees){
550
- const hashesAtFee = this.#pendingByPriority.get(fee);
551
- const sortedHashes = [
552
- ...hashesAtFee
553
- ].sort(hashCompareFn);
554
- for (const hash of sortedHashes){
555
- yield hash;
556
- }
564
+ * Deletes a transaction from both indices and DB.
565
+ * Emits onTxsRemoved callback immediately after DB delete.
566
+ */ /**
567
+ * Deletes a transaction from the pool.
568
+ * Delegates to DeletedPool which decides soft vs hard delete based on whether
569
+ * the tx is from a pruned block.
570
+ */ async #deleteTx(txHashStr) {
571
+ this.#indices.remove(txHashStr);
572
+ this.#callbacks.onTxsRemoved([
573
+ txHashStr
574
+ ]);
575
+ await this.#deletedPool.deleteTx(txHashStr);
576
+ }
577
+ /** Deletes a batch of transactions, emitting callbacks individually for each. */ async #deleteTxsBatch(txHashes) {
578
+ for (const txHashStr of txHashes){
579
+ await this.#deleteTx(txHashStr);
557
580
  }
558
581
  }
559
582
  // ============================================================================
560
- // HELPER FUNCTIONS - Pipeline Step Functions
583
+ // PRIVATE HELPERS - Validation & Conflict Resolution
561
584
  // ============================================================================
562
- // --- Finding & Filtering Steps ---
563
- /** Finds all transactions mined in blocks after the given block number */ #findTxsMinedAfter(blockNumber) {
564
- const result = [];
565
- for (const meta of this.#metadata.values()){
566
- if (meta.minedL2BlockId !== undefined && meta.minedL2BlockId.number > blockNumber) {
567
- result.push(meta);
568
- }
569
- }
570
- return result;
571
- }
572
- /** Finds tx hashes mined at or before the given block number */ #findTxsMinedAtOrBefore(blockNumber) {
573
- const result = [];
574
- for (const [txHashStr, meta] of this.#metadata){
575
- if (meta.minedL2BlockId !== undefined && meta.minedL2BlockId.number <= blockNumber) {
576
- result.push(txHashStr);
577
- }
578
- }
579
- return result;
580
- }
581
- /** Finds protected tx hashes from slots earlier than the given slot number */ #findExpiredProtectedTxs(slotNumber) {
582
- const result = [];
583
- for (const [txHashStr, protectedSlot] of this.#protectedTransactions){
584
- if (protectedSlot < slotNumber) {
585
- result.push(txHashStr);
586
- }
587
- }
588
- return result;
589
- }
590
- /** Filters out transactions that are currently protected */ #filterUnprotected(txs) {
591
- return txs.filter((meta)=>!this.#protectedTransactions.has(meta.txHash));
592
- }
593
- /** Filters to transactions that have metadata and are not mined */ #filterRestorable(txHashes) {
594
- const result = [];
595
- for (const txHashStr of txHashes){
596
- const meta = this.#metadata.get(txHashStr);
597
- if (meta && meta.minedL2BlockId === undefined) {
598
- result.push(meta);
599
- }
585
+ /** Validates transaction metadata, returning true if valid */ async #validateMeta(meta, validator, context) {
586
+ const txValidator = validator ?? await this.#createTxValidator();
587
+ const result = await txValidator.validateTx(meta);
588
+ if (result.result !== 'valid') {
589
+ const contextStr = context ? ` ${context}` : '';
590
+ this.#log.info(`Tx ${meta.txHash}${contextStr} failed validation: ${result.reason?.join(', ')}`);
591
+ return false;
600
592
  }
601
- return result;
593
+ return true;
602
594
  }
603
- // --- Validation & Conflict Resolution Steps ---
604
- /** Validates transactions for pending pool, returning valid and invalid groups */ async #validateForPending(txs) {
595
+ /** Validates metadata directly */ async #revalidateMetadata(metas, context) {
605
596
  const valid = [];
606
597
  const invalid = [];
607
- for (const meta of txs){
608
- const buffer = await this.#txsDB.getAsync(meta.txHash);
609
- if (!buffer) {
610
- this.#log.warn(`Tx ${meta.txHash} not found in DB during validation`);
611
- invalid.push(meta.txHash);
612
- continue;
613
- }
614
- const tx = Tx.fromBuffer(buffer);
615
- const result = await this.#pendingTxValidator.validateTx(tx);
616
- if (result.result === 'valid') {
598
+ const validator = await this.#createTxValidator();
599
+ for (const meta of metas){
600
+ if (await this.#validateMeta(meta, validator, context)) {
617
601
  valid.push(meta);
618
602
  } else {
619
- this.#log.info(`Tx ${meta.txHash} failed validation: ${result.reason?.join(', ')}`);
620
603
  invalid.push(meta.txHash);
621
604
  }
622
605
  }
@@ -633,7 +616,7 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
633
616
  const added = [];
634
617
  const toEvict = [];
635
618
  for (const meta of txs){
636
- const conflict = checkNullifierConflict(meta, (nullifier)=>this.#nullifierToTxHash.get(nullifier), (txHash)=>this.#metadata.get(txHash));
619
+ const conflict = checkNullifierConflict(meta, (nullifier)=>this.#indices.getTxHashByNullifier(nullifier), (txHash)=>this.#indices.getMetadata(txHash));
637
620
  if (conflict.shouldIgnore) {
638
621
  // Lower priority than existing - don't add, mark for deletion
639
622
  toEvict.push(meta.txHash);
@@ -642,13 +625,13 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
642
625
  toEvict.push(...conflict.txHashesToEvict);
643
626
  // Remove evicted from indices immediately for subsequent checks
644
627
  for (const evictHash of conflict.txHashesToEvict){
645
- const evictMeta = this.#metadata.get(evictHash);
628
+ const evictMeta = this.#indices.getMetadata(evictHash);
646
629
  if (evictMeta) {
647
- this.#removeFromPendingIndices(evictMeta);
630
+ this.#indices.removeFromPendingIndices(evictMeta);
648
631
  }
649
632
  }
650
633
  // Add to pending indices immediately so subsequent txs in the batch see this tx
651
- this.#addToPendingIndices(meta);
634
+ this.#indices.addToPendingIndices(meta);
652
635
  added.push(meta);
653
636
  }
654
637
  }
@@ -657,32 +640,10 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
657
640
  toEvict
658
641
  };
659
642
  }
660
- // --- State Transition Steps ---
661
- /** Clears the mined status from transactions, returning them for further processing */ #unmineTxs(txs) {
662
- for (const meta of txs){
663
- meta.minedL2BlockId = undefined;
664
- }
665
- return txs;
666
- }
667
- /** Removes protection from tx hashes and clears them from the protected map */ #clearProtection(txHashes) {
668
- for (const txHashStr of txHashes){
669
- this.#protectedTransactions.delete(txHashStr);
670
- }
671
- }
672
- // --- Batch Operation Steps ---
673
- /** Deletes a batch of transactions permanently */ async #deleteTxsBatch(txHashes) {
674
- if (txHashes.length === 0) {
675
- return;
676
- }
677
- await this.#store.transactionAsync(async ()=>{
678
- for (const txHashStr of txHashes){
679
- await this.#deleteTx(txHashStr);
680
- }
681
- });
682
- this.#callbacks.onTxsRemoved(txHashes);
683
- }
684
- // --- Block & Tx Info Steps ---
685
- /** Builds a block ID from a block header */ async #buildBlockId(block) {
643
+ // ============================================================================
644
+ // PRIVATE HELPERS - Block & Hydration
645
+ // ============================================================================
646
+ async #buildBlockId(block) {
686
647
  return {
687
648
  number: block.globalVariables.blockNumber,
688
649
  hash: (await block.hash()).toString()
@@ -698,40 +659,14 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
698
659
  hash: txEffect.l2BlockHash.toString()
699
660
  };
700
661
  }
701
- /** Marks a batch of transactions as mined */ #markTxsAsMined(metas, blockId) {
702
- for (const meta of metas){
703
- this.#markAsMined(meta, blockId);
704
- }
705
- }
706
- // --- Add Transaction Steps ---
707
- /** Persists a transaction to the database */ async #persistTx(txHashStr, tx) {
708
- await this.#txsDB.set(txHashStr, tx.toBuffer());
709
- }
710
- /** Adds a new transaction as protected, returning its metadata */ async #addNewProtectedTx(tx, slotNumber) {
711
- const txHashStr = tx.getTxHash().toString();
712
- const meta = await buildTxMetaData(tx);
713
- this.#protectedTransactions.set(txHashStr, slotNumber);
714
- await this.#persistTx(txHashStr, tx);
715
- this.#metadata.set(txHashStr, meta);
716
- // Don't add to pending indices since it's protected
717
- this.#log.verbose(`Added protected tx ${txHashStr} for slot ${slotNumber}`);
718
- return meta;
719
- }
720
- /** Adds a new transaction as mined, returning its metadata */ async #addNewMinedTx(tx, blockId) {
721
- const txHashStr = tx.getTxHash().toString();
722
- const meta = await buildTxMetaData(tx);
723
- meta.minedL2BlockId = blockId;
724
- await this.#persistTx(txHashStr, tx);
725
- this.#metadata.set(txHashStr, meta);
726
- // Don't add to pending indices since it's mined
727
- this.#log.verbose(`Added mined tx ${txHashStr} from block ${blockId.number}`);
728
- return meta;
729
- }
730
- // --- Hydration Steps ---
731
662
  /** Loads all transactions from the database, returning loaded txs and deserialization errors */ async #loadAllTxsFromDb() {
732
663
  const loaded = [];
733
664
  const errors = [];
734
665
  for await (const [txHashStr, buffer] of this.#txsDB.entriesAsync()){
666
+ // Skip soft-deleted transactions - they stay in DB but not in indices
667
+ if (this.#deletedPool.isSoftDeleted(txHashStr)) {
668
+ continue;
669
+ }
735
670
  try {
736
671
  const tx = Tx.fromBuffer(buffer);
737
672
  const meta = await buildTxMetaData(tx);
@@ -768,43 +703,6 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
768
703
  }
769
704
  }
770
705
  }
771
- /** Partitions transactions by mined status */ #partitionByMinedStatus(txs) {
772
- const mined = [];
773
- const nonMined = [];
774
- for (const entry of txs){
775
- if (entry.meta.minedL2BlockId !== undefined) {
776
- mined.push(entry.meta);
777
- } else {
778
- nonMined.push(entry);
779
- }
780
- }
781
- return {
782
- mined,
783
- nonMined
784
- };
785
- }
786
- /** Validates non-mined transactions, returning valid metadata and invalid hashes */ async #validateNonMinedTxs(txs) {
787
- const valid = [];
788
- const invalid = [];
789
- for (const { tx, meta } of txs){
790
- const result = await this.#pendingTxValidator.validateTx(tx);
791
- if (result.result === 'valid') {
792
- valid.push(meta);
793
- } else {
794
- this.#log.info(`Removing invalid tx ${meta.txHash} on startup: ${result.reason?.join(', ')}`);
795
- invalid.push(meta.txHash);
796
- }
797
- }
798
- return {
799
- valid,
800
- invalid
801
- };
802
- }
803
- /** Populates metadata index for mined transactions */ #populateMinedIndices(metas) {
804
- for (const meta of metas){
805
- this.#metadata.set(meta.txHash, meta);
806
- }
807
- }
808
706
  /**
809
707
  * Rebuilds the pending pool by processing each tx through pre-add rules.
810
708
  * Starts with an empty pending pool and adds txs one by one, resolving conflicts.
@@ -824,17 +722,17 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
824
722
  }
825
723
  // Evict any conflicting txs identified by pre-add rules
826
724
  for (const evictHashStr of preAddResult.txHashesToEvict){
827
- const evictMeta = this.#metadata.get(evictHashStr);
725
+ const evictMeta = this.#indices.getMetadata(evictHashStr);
828
726
  if (evictMeta) {
829
- this.#removeFromPendingIndices(evictMeta);
830
- this.#metadata.delete(evictHashStr);
727
+ this.#indices.removeFromPendingIndices(evictMeta);
728
+ this.#indices.remove(evictHashStr);
831
729
  rejected.push(evictHashStr);
832
730
  accepted.delete(evictHashStr);
833
731
  this.#log.debug(`Evicted tx ${evictHashStr} during rebuild due to conflict with ${meta.txHash}`);
834
732
  }
835
733
  }
836
- // Add to metadata and pending indices
837
- this.#addToIndices(meta);
734
+ // Add to indices
735
+ this.#indices.addPending(meta);
838
736
  accepted.add(meta.txHash);
839
737
  }
840
738
  this.#log.info(`Rebuilt pending pool: ${accepted.size} accepted, ${rejected.length} rejected`);
@@ -845,197 +743,38 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
845
743
  rejected
846
744
  };
847
745
  }
848
- // --- Add Pending Tx Steps ---
849
- /** Checks if a tx is a duplicate (already in pool) */ #isDuplicateTx(txHashStr) {
850
- return this.#metadata.has(txHashStr);
851
- }
852
- /** Adds a new pending tx to the pool, returning its metadata */ async #addNewPendingTx(tx) {
853
- const txHashStr = tx.getTxHash().toString();
854
- const meta = await buildTxMetaData(tx);
855
- await this.#persistTx(txHashStr, tx);
856
- this.#addToIndices(meta);
857
- this.#log.verbose(`Added tx ${txHashStr} to pool`, {
858
- eventName: 'tx-added-to-pool',
859
- state: this.#getTxState(meta)
860
- });
861
- return meta;
862
- }
863
- // ============================================================================
864
- // HELPER FUNCTIONS - Index Management
865
- // ============================================================================
866
- #addToIndices(meta) {
867
- this.#metadata.set(meta.txHash, meta);
868
- if (this.#getTxState(meta) === 'pending') {
869
- this.#addToPendingIndices(meta);
870
- }
871
- // Protected and mined txs don't go into pending indices
872
- }
873
- #addToPendingIndices(meta) {
874
- // Add to nullifier index
875
- for (const nullifier of meta.nullifiers){
876
- this.#nullifierToTxHash.set(nullifier, meta.txHash);
877
- }
878
- // Add to fee payer index
879
- let feePayerSet = this.#feePayerToTxHashes.get(meta.feePayer);
880
- if (!feePayerSet) {
881
- feePayerSet = new Set();
882
- this.#feePayerToTxHashes.set(meta.feePayer, feePayerSet);
883
- }
884
- feePayerSet.add(meta.txHash);
885
- // Add to priority bucket
886
- let prioritySet = this.#pendingByPriority.get(meta.priorityFee);
887
- if (!prioritySet) {
888
- prioritySet = new Set();
889
- this.#pendingByPriority.set(meta.priorityFee, prioritySet);
890
- }
891
- prioritySet.add(meta.txHash);
892
- }
893
- #removeFromPendingIndices(meta) {
894
- // Remove from nullifier index
895
- for (const nullifier of meta.nullifiers){
896
- this.#nullifierToTxHash.delete(nullifier);
897
- }
898
- // Remove from fee payer index
899
- const feePayerSet = this.#feePayerToTxHashes.get(meta.feePayer);
900
- if (feePayerSet) {
901
- feePayerSet.delete(meta.txHash);
902
- if (feePayerSet.size === 0) {
903
- this.#feePayerToTxHashes.delete(meta.feePayer);
904
- }
905
- }
906
- // Remove from priority map
907
- const hashSet = this.#pendingByPriority.get(meta.priorityFee);
908
- if (hashSet) {
909
- hashSet.delete(meta.txHash);
910
- if (hashSet.size === 0) {
911
- this.#pendingByPriority.delete(meta.priorityFee);
912
- }
913
- }
914
- }
915
- #updateProtection(txHashStr, slotNumber) {
916
- const currentSlot = this.#protectedTransactions.get(txHashStr);
917
- // Only update if not already protected at an equal or later slot
918
- if (currentSlot !== undefined && currentSlot >= slotNumber) {
919
- return;
920
- }
921
- // Remove from pending indices if transitioning from pending to protected
922
- if (currentSlot === undefined) {
923
- const meta = this.#metadata.get(txHashStr);
924
- if (meta) {
925
- this.#removeFromPendingIndices(meta);
926
- }
927
- }
928
- this.#protectedTransactions.set(txHashStr, slotNumber);
929
- }
930
- #markAsMined(meta, blockId) {
931
- meta.minedL2BlockId = blockId;
932
- // Safe to call unconditionally - removeFromPendingIndices is idempotent
933
- this.#removeFromPendingIndices(meta);
934
- }
935
- async #deleteTx(txHashStr) {
936
- const meta = this.#metadata.get(txHashStr);
937
- if (!meta) {
938
- return;
939
- }
940
- // Remove from all indices
941
- this.#metadata.delete(txHashStr);
942
- this.#protectedTransactions.delete(txHashStr);
943
- this.#removeFromPendingIndices(meta);
944
- // Remove from persistence
945
- await this.#txsDB.delete(txHashStr);
946
- }
947
746
  // ============================================================================
948
- // HELPER FUNCTIONS - Adapters
747
+ // PRIVATE HELPERS - Pool Access Adapters
949
748
  // ============================================================================
950
- /** Gets all pending transactions for a given fee payer. */ #getFeePayerPendingTxs(feePayer) {
951
- const txHashes = this.#feePayerToTxHashes.get(feePayer);
952
- if (!txHashes) {
953
- return [];
954
- }
955
- const result = [];
956
- for (const txHashStr of txHashes){
957
- const meta = this.#metadata.get(txHashStr);
958
- if (meta && this.#getTxState(meta) === 'pending') {
959
- result.push(meta);
960
- }
961
- }
962
- return result;
963
- }
964
- /**
965
- * Creates a PoolOperations adapter for use with the eviction manager.
966
- */ #createPoolOperations() {
749
+ #createPoolOperations() {
967
750
  return {
968
- getPendingTxs: ()=>{
969
- const result = [];
970
- for (const hashSet of this.#pendingByPriority.values()){
971
- for (const txHashStr of hashSet){
972
- const meta = this.#metadata.get(txHashStr);
973
- if (meta) {
974
- result.push(meta);
975
- }
976
- }
977
- }
978
- return result;
979
- },
980
- getPendingFeePayers: ()=>{
981
- return Array.from(this.#feePayerToTxHashes.keys());
982
- },
983
- getFeePayerPendingTxs: (feePayer)=>{
984
- return this.#getFeePayerPendingTxs(feePayer);
985
- },
986
- getPendingTxCount: ()=>{
987
- return this.getPendingTxCount();
988
- },
989
- getLowestPriorityPending: (limit)=>{
990
- return this.getLowestPriorityPending(limit).map((h)=>h.toString());
991
- },
992
- deleteTxs: async (txHashes)=>{
993
- await this.#store.transactionAsync(async ()=>{
994
- for (const txHashStr of txHashes){
995
- await this.#deleteTx(txHashStr);
996
- }
997
- });
998
- this.#callbacks.onTxsRemoved(txHashes);
999
- }
751
+ getPendingTxs: ()=>this.#indices.getPendingTxs(),
752
+ getPendingFeePayers: ()=>this.#indices.getPendingFeePayers(),
753
+ getFeePayerPendingTxs: (feePayer)=>this.#indices.getFeePayerPendingTxs(feePayer),
754
+ getPendingTxCount: ()=>this.#indices.getPendingTxCount(),
755
+ getLowestPriorityPending: (limit)=>this.#indices.getLowestPriorityPending(limit),
756
+ deleteTxs: (txHashes)=>this.#deleteTxsBatch(txHashes)
1000
757
  };
1001
758
  }
1002
- /**
1003
- * Creates a PreAddPoolAccess adapter for use with pre-add eviction rules.
1004
- * All methods work with strings and TxMetaData for efficiency.
1005
- */ #createPreAddPoolAccess() {
759
+ #createPreAddPoolAccess() {
1006
760
  return {
1007
761
  getMetadata: (txHashStr)=>{
1008
- const meta = this.#metadata.get(txHashStr);
1009
- if (!meta || this.#getTxState(meta) !== 'pending') {
762
+ const meta = this.#indices.getMetadata(txHashStr);
763
+ if (!meta || this.#indices.getTxState(meta) !== 'pending') {
1010
764
  return undefined;
1011
765
  }
1012
766
  return meta;
1013
767
  },
1014
- getTxHashByNullifier: (nullifier)=>{
1015
- return this.#nullifierToTxHash.get(nullifier);
1016
- },
768
+ getTxHashByNullifier: (nullifier)=>this.#indices.getTxHashByNullifier(nullifier),
1017
769
  getFeePayerBalance: async (feePayer)=>{
1018
770
  const db = this.#worldStateSynchronizer.getCommitted();
1019
771
  const publicStateSource = new DatabasePublicStateSource(db);
1020
772
  const balance = await publicStateSource.storageRead(ProtocolContractAddress.FeeJuice, await computeFeePayerBalanceStorageSlot(AztecAddress.fromString(feePayer)));
1021
773
  return balance.toBigInt();
1022
774
  },
1023
- getFeePayerPendingTxs: (feePayer)=>{
1024
- return this.#getFeePayerPendingTxs(feePayer);
1025
- },
1026
- getPendingTxCount: ()=>{
1027
- return this.getPendingTxCount();
1028
- },
1029
- getLowestPriorityPendingTx: ()=>{
1030
- // Iterate in ascending order to find the lowest priority
1031
- for (const txHashStr of this.#iteratePendingByPriority('asc')){
1032
- const meta = this.#metadata.get(txHashStr);
1033
- if (meta) {
1034
- return meta;
1035
- }
1036
- }
1037
- return undefined;
1038
- }
775
+ getFeePayerPendingTxs: (feePayer)=>this.#indices.getFeePayerPendingTxs(feePayer),
776
+ getPendingTxCount: ()=>this.#indices.getPendingTxCount(),
777
+ getLowestPriorityPendingTx: ()=>this.#indices.getLowestPriorityPendingTx()
1039
778
  };
1040
779
  }
1041
780
  }