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

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 (216) 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 +40 -9
  4. package/dest/client/interface.d.ts +33 -15
  5. package/dest/client/interface.d.ts.map +1 -1
  6. package/dest/client/p2p_client.d.ts +33 -36
  7. package/dest/client/p2p_client.d.ts.map +1 -1
  8. package/dest/client/p2p_client.js +104 -139
  9. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +1 -1
  10. package/dest/config.d.ts +14 -5
  11. package/dest/config.d.ts.map +1 -1
  12. package/dest/config.js +11 -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 +102 -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 +436 -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 +87 -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 +180 -0
  33. package/dest/mem_pools/tx_pool_v2/index.d.ts +2 -1
  34. package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -1
  35. package/dest/mem_pools/tx_pool_v2/index.js +1 -0
  36. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +7 -3
  37. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
  38. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +25 -3
  39. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
  40. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +38 -5
  41. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +99 -0
  42. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -0
  43. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +332 -0
  44. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +1 -1
  45. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
  46. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +6 -0
  47. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +1 -1
  48. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
  49. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +226 -516
  50. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +3 -3
  51. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
  52. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +3 -3
  53. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
  54. package/dest/msg_validators/tx_validator/block_header_validator.d.ts +16 -3
  55. package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
  56. package/dest/msg_validators/tx_validator/block_header_validator.js +1 -1
  57. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +13 -3
  58. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
  59. package/dest/msg_validators/tx_validator/double_spend_validator.js +4 -4
  60. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +20 -4
  61. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -1
  62. package/dest/msg_validators/tx_validator/timestamp_validator.js +2 -2
  63. package/dest/services/dummy_service.d.ts +10 -2
  64. package/dest/services/dummy_service.d.ts.map +1 -1
  65. package/dest/services/dummy_service.js +6 -0
  66. package/dest/services/encoding.d.ts +2 -2
  67. package/dest/services/encoding.d.ts.map +1 -1
  68. package/dest/services/encoding.js +2 -2
  69. package/dest/services/gossipsub/index.d.ts +3 -0
  70. package/dest/services/gossipsub/index.d.ts.map +1 -0
  71. package/dest/services/gossipsub/index.js +2 -0
  72. package/dest/services/gossipsub/scoring.d.ts +21 -3
  73. package/dest/services/gossipsub/scoring.d.ts.map +1 -1
  74. package/dest/services/gossipsub/scoring.js +24 -7
  75. package/dest/services/gossipsub/topic_score_params.d.ts +161 -0
  76. package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -0
  77. package/dest/services/gossipsub/topic_score_params.js +324 -0
  78. package/dest/services/libp2p/libp2p_service.d.ts +84 -35
  79. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  80. package/dest/services/libp2p/libp2p_service.js +368 -273
  81. package/dest/services/peer-manager/peer_scoring.d.ts +1 -1
  82. package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
  83. package/dest/services/peer-manager/peer_scoring.js +25 -2
  84. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +4 -4
  85. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
  86. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +8 -8
  87. package/dest/services/reqresp/interface.d.ts +10 -1
  88. package/dest/services/reqresp/interface.d.ts.map +1 -1
  89. package/dest/services/reqresp/interface.js +15 -1
  90. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +7 -5
  91. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
  92. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +16 -11
  93. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +21 -10
  94. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
  95. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +27 -11
  96. package/dest/services/reqresp/protocols/tx.d.ts +7 -1
  97. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
  98. package/dest/services/reqresp/protocols/tx.js +20 -0
  99. package/dest/services/reqresp/reqresp.d.ts +1 -1
  100. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  101. package/dest/services/reqresp/reqresp.js +11 -4
  102. package/dest/services/service.d.ts +35 -1
  103. package/dest/services/service.d.ts.map +1 -1
  104. package/dest/services/tx_collection/config.d.ts +10 -4
  105. package/dest/services/tx_collection/config.d.ts.map +1 -1
  106. package/dest/services/tx_collection/config.js +19 -3
  107. package/dest/services/tx_collection/fast_tx_collection.d.ts +6 -5
  108. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
  109. package/dest/services/tx_collection/fast_tx_collection.js +27 -17
  110. package/dest/services/tx_collection/file_store_tx_collection.d.ts +44 -0
  111. package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -0
  112. package/dest/services/tx_collection/file_store_tx_collection.js +118 -0
  113. package/dest/services/tx_collection/file_store_tx_source.d.ts +27 -0
  114. package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -0
  115. package/dest/services/tx_collection/file_store_tx_source.js +57 -0
  116. package/dest/services/tx_collection/index.d.ts +3 -2
  117. package/dest/services/tx_collection/index.d.ts.map +1 -1
  118. package/dest/services/tx_collection/index.js +1 -0
  119. package/dest/services/tx_collection/proposal_tx_collector.d.ts +12 -12
  120. package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +1 -1
  121. package/dest/services/tx_collection/proposal_tx_collector.js +4 -5
  122. package/dest/services/tx_collection/slow_tx_collection.d.ts +3 -1
  123. package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -1
  124. package/dest/services/tx_collection/slow_tx_collection.js +48 -19
  125. package/dest/services/tx_collection/tx_collection.d.ts +17 -7
  126. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
  127. package/dest/services/tx_collection/tx_collection.js +58 -2
  128. package/dest/services/tx_collection/tx_collection_sink.d.ts +15 -6
  129. package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
  130. package/dest/services/tx_collection/tx_collection_sink.js +13 -7
  131. package/dest/services/tx_file_store/config.d.ts +1 -3
  132. package/dest/services/tx_file_store/config.d.ts.map +1 -1
  133. package/dest/services/tx_file_store/config.js +0 -4
  134. package/dest/services/tx_file_store/tx_file_store.d.ts +3 -3
  135. package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -1
  136. package/dest/services/tx_provider.d.ts +3 -3
  137. package/dest/services/tx_provider.d.ts.map +1 -1
  138. package/dest/services/tx_provider.js +5 -4
  139. package/dest/test-helpers/make-test-p2p-clients.d.ts +3 -3
  140. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  141. package/dest/test-helpers/mock-pubsub.d.ts +27 -1
  142. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  143. package/dest/test-helpers/mock-pubsub.js +97 -2
  144. package/dest/test-helpers/reqresp-nodes.d.ts +1 -1
  145. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  146. package/dest/test-helpers/reqresp-nodes.js +2 -1
  147. package/dest/test-helpers/testbench-utils.d.ts +38 -38
  148. package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
  149. package/dest/test-helpers/testbench-utils.js +124 -61
  150. package/dest/testbench/p2p_client_testbench_worker.js +2 -2
  151. package/package.json +14 -14
  152. package/src/client/factory.ts +68 -12
  153. package/src/client/interface.ts +39 -14
  154. package/src/client/p2p_client.ts +139 -162
  155. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +1 -1
  156. package/src/config.ts +26 -2
  157. package/src/index.ts +1 -0
  158. package/src/mem_pools/attestation_pool/attestation_pool.ts +488 -91
  159. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +442 -102
  160. package/src/mem_pools/attestation_pool/index.ts +9 -2
  161. package/src/mem_pools/index.ts +4 -1
  162. package/src/mem_pools/interface.ts +4 -4
  163. package/src/mem_pools/tx_pool_v2/README.md +87 -16
  164. package/src/mem_pools/tx_pool_v2/deleted_pool.ts +234 -0
  165. package/src/mem_pools/tx_pool_v2/index.ts +1 -0
  166. package/src/mem_pools/tx_pool_v2/interfaces.ts +4 -2
  167. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +53 -6
  168. package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +417 -0
  169. package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +3 -0
  170. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +234 -604
  171. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +2 -2
  172. package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +2 -2
  173. package/src/msg_validators/tx_validator/block_header_validator.ts +15 -3
  174. package/src/msg_validators/tx_validator/double_spend_validator.ts +11 -6
  175. package/src/msg_validators/tx_validator/timestamp_validator.ts +19 -14
  176. package/src/services/dummy_service.ts +12 -0
  177. package/src/services/encoding.ts +2 -2
  178. package/src/services/gossipsub/README.md +626 -0
  179. package/src/services/gossipsub/index.ts +2 -0
  180. package/src/services/gossipsub/scoring.ts +29 -5
  181. package/src/services/gossipsub/topic_score_params.ts +451 -0
  182. package/src/services/libp2p/libp2p_service.ts +370 -275
  183. package/src/services/peer-manager/peer_scoring.ts +25 -0
  184. package/src/services/reqresp/batch-tx-requester/README.md +7 -7
  185. package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +11 -11
  186. package/src/services/reqresp/interface.ts +26 -1
  187. package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +23 -14
  188. package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +38 -15
  189. package/src/services/reqresp/protocols/tx.ts +22 -0
  190. package/src/services/reqresp/reqresp.ts +13 -3
  191. package/src/services/service.ts +40 -0
  192. package/src/services/tx_collection/config.ts +32 -6
  193. package/src/services/tx_collection/fast_tx_collection.ts +28 -26
  194. package/src/services/tx_collection/file_store_tx_collection.ts +152 -0
  195. package/src/services/tx_collection/file_store_tx_source.ts +70 -0
  196. package/src/services/tx_collection/index.ts +2 -1
  197. package/src/services/tx_collection/proposal_tx_collector.ts +12 -14
  198. package/src/services/tx_collection/slow_tx_collection.ts +55 -26
  199. package/src/services/tx_collection/tx_collection.ts +78 -12
  200. package/src/services/tx_collection/tx_collection_sink.ts +17 -7
  201. package/src/services/tx_file_store/config.ts +0 -6
  202. package/src/services/tx_file_store/tx_file_store.ts +4 -4
  203. package/src/services/tx_provider.ts +8 -7
  204. package/src/test-helpers/make-test-p2p-clients.ts +3 -3
  205. package/src/test-helpers/mock-pubsub.ts +133 -3
  206. package/src/test-helpers/reqresp-nodes.ts +2 -1
  207. package/src/test-helpers/testbench-utils.ts +122 -74
  208. package/src/testbench/p2p_client_testbench_worker.ts +2 -2
  209. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +0 -40
  210. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +0 -1
  211. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +0 -218
  212. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +0 -31
  213. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +0 -1
  214. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +0 -180
  215. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +0 -320
  216. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +0 -264
@@ -1,4 +1,4 @@
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
3
  import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
4
4
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
@@ -10,6 +10,7 @@ import { DatabasePublicStateSource } from '@aztec/stdlib/trees';
10
10
  import { BlockHeader, Tx, TxHash, type TxValidator } from '@aztec/stdlib/tx';
11
11
 
12
12
  import { TxArchive } from './archive/index.js';
13
+ import { DeletedPool } from './deleted_pool.js';
13
14
  import {
14
15
  EvictionManager,
15
16
  FeePayerBalanceEvictionRule,
@@ -29,14 +30,8 @@ import {
29
30
  type TxPoolV2Config,
30
31
  type TxPoolV2Dependencies,
31
32
  } from './interfaces.js';
32
- import {
33
- type TxMetaData,
34
- type TxState,
35
- buildTxMetaData,
36
- checkNullifierConflict,
37
- compareFee,
38
- compareTxHash,
39
- } from './tx_metadata.js';
33
+ import { type TxMetaData, type TxState, buildTxMetaData, checkNullifierConflict } from './tx_metadata.js';
34
+ import { TxPoolIndices } from './tx_pool_indices.js';
40
35
 
41
36
  /**
42
37
  * Callbacks for the implementation to notify the outer class about events and metrics.
@@ -59,26 +54,15 @@ export class TxPoolV2Impl {
59
54
  // === Dependencies ===
60
55
  #l2BlockSource: L2BlockSource;
61
56
  #worldStateSynchronizer: WorldStateSynchronizer;
62
- #pendingTxValidator: TxValidator<Tx>;
57
+ #createTxValidator: TxPoolV2Dependencies['createTxValidator'];
63
58
 
64
59
  // === 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();
60
+ #indices: TxPoolIndices = new TxPoolIndices();
78
61
 
79
62
  // === Config & Services ===
80
63
  #config: TxPoolV2Config;
81
64
  #archive: TxArchive;
65
+ #deletedPool: DeletedPool;
82
66
  #evictionManager: EvictionManager;
83
67
  #log: Logger;
84
68
  #callbacks: TxPoolV2Callbacks;
@@ -96,10 +80,11 @@ export class TxPoolV2Impl {
96
80
 
97
81
  this.#l2BlockSource = deps.l2BlockSource;
98
82
  this.#worldStateSynchronizer = deps.worldStateSynchronizer;
99
- this.#pendingTxValidator = deps.pendingTxValidator;
83
+ this.#createTxValidator = deps.createTxValidator;
100
84
 
101
85
  this.#config = { ...DEFAULT_TX_POOL_V2_CONFIG, ...config };
102
86
  this.#archive = new TxArchive(archiveStore, this.#config.archivedTxLimit, log);
87
+ this.#deletedPool = new DeletedPool(store, this.#txsDB, log);
103
88
  this.#log = log;
104
89
  this.#callbacks = callbacks;
105
90
 
@@ -134,26 +119,42 @@ export class TxPoolV2Impl {
134
119
  * by running pre-add rules to resolve nullifier conflicts, balance checks, and pool size limits.
135
120
  */
136
121
  async hydrateFromDatabase(): Promise<void> {
137
- // Step 1: Load all transactions from DB
122
+ // Step 0: Hydrate deleted pool state
123
+ await this.#deletedPool.hydrateFromDatabase();
124
+
125
+ // Step 1: Load all transactions from DB (excluding soft-deleted)
138
126
  const { loaded, errors: deserializationErrors } = await this.#loadAllTxsFromDb();
139
127
 
140
128
  // Step 2: Check mined status for each tx
141
129
  await this.#markMinedStatusBatch(loaded.map(l => l.meta));
142
130
 
143
131
  // Step 3: Partition by mined status
144
- const { mined, nonMined } = this.#partitionByMinedStatus(loaded);
132
+ const mined: TxMetaData[] = [];
133
+ const nonMined: { tx: Tx; meta: TxMetaData }[] = [];
134
+ for (const entry of loaded) {
135
+ if (entry.meta.minedL2BlockId !== undefined) {
136
+ mined.push(entry.meta);
137
+ } else {
138
+ nonMined.push(entry);
139
+ }
140
+ }
145
141
 
146
142
  // Step 4: Validate non-mined transactions
147
- const { valid, invalid } = await this.#validateNonMinedTxs(nonMined);
143
+ const { valid, invalid } = await this.#revalidateMetadata(
144
+ nonMined.map(e => e.meta),
145
+ 'on startup',
146
+ );
148
147
 
149
148
  // Step 5: Populate mined indices (these don't need conflict resolution)
150
- this.#populateMinedIndices(mined);
149
+ for (const meta of mined) {
150
+ this.#indices.addMined(meta);
151
+ }
151
152
 
152
153
  // Step 6: Rebuild pending pool by running pre-add rules for each tx
153
154
  // This resolves nullifier conflicts, fee payer balance issues, and pool size limits
154
155
  const { rejected } = await this.#rebuildPendingPool(valid);
155
156
 
156
- // Step 7: Delete invalid and rejected txs from DB
157
+ // Step 7: Delete invalid and rejected txs from DB only (indices were never populated for these)
157
158
  const toDelete = [...deserializationErrors, ...invalid, ...rejected];
158
159
  if (toDelete.length === 0) {
159
160
  return;
@@ -170,7 +171,6 @@ export class TxPoolV2Impl {
170
171
  const accepted: TxHash[] = [];
171
172
  const ignored: TxHash[] = [];
172
173
  const rejected: TxHash[] = [];
173
- const newlyAdded: Tx[] = [];
174
174
  const acceptedPending = new Set<string>();
175
175
 
176
176
  const poolAccess = this.#createPreAddPoolAccess();
@@ -181,31 +181,28 @@ export class TxPoolV2Impl {
181
181
  const txHashStr = txHash.toString();
182
182
 
183
183
  // Skip duplicates
184
- if (this.#isDuplicateTx(txHashStr)) {
184
+ if (this.#indices.has(txHashStr)) {
185
185
  ignored.push(txHash);
186
186
  continue;
187
187
  }
188
188
 
189
189
  // Check mined status first (applies to all paths)
190
190
  const minedBlockId = await this.#getMinedBlockId(txHash);
191
- const preProtectedSlot = this.#protectedTransactions.get(txHashStr);
191
+ const preProtectedSlot = this.#indices.getProtectionSlot(txHashStr);
192
192
 
193
193
  if (minedBlockId) {
194
194
  // Already mined - add directly (protection already set if pre-protected)
195
- await this.#addNewMinedTx(tx, minedBlockId);
195
+ await this.#addTx(tx, { mined: minedBlockId }, opts);
196
196
  accepted.push(txHash);
197
- newlyAdded.push(tx);
198
197
  } else if (preProtectedSlot !== undefined) {
199
198
  // Pre-protected and not mined - add as protected (bypass validation)
200
- await this.#addNewProtectedTx(tx, preProtectedSlot);
199
+ await this.#addTx(tx, { protected: preProtectedSlot }, opts);
201
200
  accepted.push(txHash);
202
- newlyAdded.push(tx);
203
201
  } else {
204
202
  // Regular pending tx - validate and run pre-add rules
205
- const result = await this.#tryAddRegularPendingTx(tx, poolAccess, acceptedPending, ignored);
203
+ const result = await this.#tryAddRegularPendingTx(tx, opts, poolAccess, acceptedPending, ignored);
206
204
  if (result.status === 'accepted') {
207
205
  acceptedPending.add(txHashStr);
208
- newlyAdded.push(tx);
209
206
  } else if (result.status === 'rejected') {
210
207
  rejected.push(txHash);
211
208
  } else {
@@ -222,22 +219,18 @@ export class TxPoolV2Impl {
222
219
 
223
220
  // Run post-add eviction rules for pending txs
224
221
  if (acceptedPending.size > 0) {
225
- const feePayers = Array.from(acceptedPending).map(txHash => this.#metadata.get(txHash)!.feePayer);
222
+ const feePayers = Array.from(acceptedPending).map(txHash => this.#indices.getMetadata(txHash)!.feePayer);
226
223
  const uniqueFeePayers = new Set<string>(feePayers);
227
224
  await this.#evictionManager.evictAfterNewTxs(Array.from(acceptedPending), [...uniqueFeePayers]);
228
225
  }
229
226
 
230
- // Emit events
231
- if (newlyAdded.length > 0) {
232
- this.#callbacks.onTxsAdded(newlyAdded, opts);
233
- }
234
-
235
227
  return { accepted, ignored, rejected };
236
228
  }
237
229
 
238
230
  /** Validates and adds a regular pending tx. Returns status. */
239
231
  async #tryAddRegularPendingTx(
240
232
  tx: Tx,
233
+ opts: { source?: string },
241
234
  poolAccess: PreAddPoolAccess,
242
235
  acceptedPending: Set<string>,
243
236
  ignored: TxHash[],
@@ -245,15 +238,13 @@ export class TxPoolV2Impl {
245
238
  const txHash = tx.getTxHash();
246
239
  const txHashStr = txHash.toString();
247
240
 
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(', ')}`);
241
+ // Build metadata and validate using metadata
242
+ const meta = await buildTxMetaData(tx);
243
+ if (!(await this.#validateMeta(meta))) {
252
244
  return { status: 'rejected' };
253
245
  }
254
246
 
255
- // Build metadata and run pre-add rules
256
- const meta = await buildTxMetaData(tx);
247
+ // Run pre-add rules
257
248
  const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
258
249
 
259
250
  if (preAddResult.shouldIgnore) {
@@ -261,18 +252,19 @@ export class TxPoolV2Impl {
261
252
  return { status: 'ignored' };
262
253
  }
263
254
 
264
- // Evict conflicts (tracking intra-batch evictions)
255
+ // Evict conflicts
265
256
  for (const evictHashStr of preAddResult.txHashesToEvict) {
266
257
  await this.#deleteTx(evictHashStr);
267
258
  this.#log.debug(`Evicted tx ${evictHashStr} due to higher-fee tx ${txHashStr}`);
268
259
  if (acceptedPending.has(evictHashStr)) {
260
+ // Evicted tx was from this batch - mark as ignored in result
269
261
  acceptedPending.delete(evictHashStr);
270
262
  ignored.push(TxHash.fromString(evictHashStr));
271
263
  }
272
264
  }
273
265
 
274
266
  // Add the transaction
275
- await this.#addNewPendingTx(tx);
267
+ await this.#addTx(tx, 'pending', opts);
276
268
  return { status: 'accepted' };
277
269
  }
278
270
 
@@ -280,18 +272,18 @@ export class TxPoolV2Impl {
280
272
  const txHashStr = tx.getTxHash().toString();
281
273
 
282
274
  // Check if already in pool
283
- if (this.#metadata.has(txHashStr)) {
275
+ if (this.#indices.has(txHashStr)) {
284
276
  return 'ignored';
285
277
  }
286
278
 
287
- // Validate transaction
288
- const validationResult = await this.#pendingTxValidator.validateTx(tx);
289
- if (validationResult.result !== 'valid') {
279
+ // Build metadata and validate using metadata
280
+ const meta = await buildTxMetaData(tx);
281
+ const validationResult = await this.#validateMeta(meta, undefined, 'can add pending');
282
+ if (validationResult !== true) {
290
283
  return 'rejected';
291
284
  }
292
285
 
293
- // Build metadata and use pre-add rules
294
- const meta = await buildTxMetaData(tx);
286
+ // Use pre-add rules
295
287
  const poolAccess = this.#createPreAddPoolAccess();
296
288
  const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
297
289
 
@@ -300,37 +292,32 @@ export class TxPoolV2Impl {
300
292
 
301
293
  async addProtectedTxs(txs: Tx[], block: BlockHeader, opts: { source?: string }): Promise<void> {
302
294
  const slotNumber = block.globalVariables.slotNumber;
303
- const newlyAdded: Tx[] = [];
304
295
 
305
296
  await this.#store.transactionAsync(async () => {
306
297
  for (const tx of txs) {
307
298
  const txHash = tx.getTxHash();
308
299
  const txHashStr = txHash.toString();
309
- const isNew = !this.#metadata.has(txHashStr);
300
+ const isNew = !this.#indices.has(txHashStr);
310
301
  const minedBlockId = await this.#getMinedBlockId(txHash);
311
302
 
312
303
  if (isNew) {
313
- // New tx - add as mined or protected
304
+ // New tx - add as mined or protected (callback emitted by #addTx)
314
305
  if (minedBlockId) {
315
- await this.#addNewMinedTx(tx, minedBlockId);
316
- this.#protectedTransactions.set(txHashStr, slotNumber);
306
+ await this.#addTx(tx, { mined: minedBlockId }, opts);
307
+ this.#indices.setProtection(txHashStr, slotNumber);
317
308
  } else {
318
- await this.#addNewProtectedTx(tx, slotNumber);
309
+ await this.#addTx(tx, { protected: slotNumber }, opts);
319
310
  }
320
- newlyAdded.push(tx);
321
311
  } else {
322
312
  // Existing tx - update protection and mined status
323
- this.#updateProtection(txHashStr, slotNumber);
313
+ this.#indices.updateProtection(txHashStr, slotNumber);
324
314
  if (minedBlockId) {
325
- this.#markAsMined(this.#metadata.get(txHashStr)!, minedBlockId);
315
+ const meta = this.#indices.getMetadata(txHashStr)!;
316
+ this.#indices.markAsMined(meta, minedBlockId);
326
317
  }
327
318
  }
328
319
  }
329
320
  });
330
-
331
- if (newlyAdded.length > 0) {
332
- this.#callbacks.onTxsAdded(newlyAdded, opts);
333
- }
334
321
  }
335
322
 
336
323
  protectTxs(txHashes: TxHash[], block: BlockHeader): TxHash[] {
@@ -340,12 +327,12 @@ export class TxPoolV2Impl {
340
327
  for (const txHash of txHashes) {
341
328
  const txHashStr = txHash.toString();
342
329
 
343
- if (this.#metadata.has(txHashStr)) {
344
- // Step 1a: Update protection for existing tx
345
- this.#updateProtection(txHashStr, slotNumber);
330
+ if (this.#indices.has(txHashStr)) {
331
+ // Update protection for existing tx
332
+ this.#indices.updateProtection(txHashStr, slotNumber);
346
333
  } else {
347
- // Step 1b: Pre-record protection for tx we don't have yet
348
- this.#protectedTransactions.set(txHashStr, slotNumber);
334
+ // Pre-record protection for tx we don't have yet
335
+ this.#indices.setProtection(txHashStr, slotNumber);
349
336
  missing.push(txHash);
350
337
  }
351
338
  }
@@ -356,28 +343,22 @@ export class TxPoolV2Impl {
356
343
  async addMinedTxs(txs: Tx[], block: BlockHeader, opts: { source?: string }): Promise<void> {
357
344
  // Step 1: Build block ID
358
345
  const blockId = await this.#buildBlockId(block);
359
- const newlyAdded: Tx[] = [];
360
346
 
361
347
  await this.#store.transactionAsync(async () => {
362
348
  for (const tx of txs) {
363
349
  const txHashStr = tx.getTxHash().toString();
364
- const existingMeta = this.#metadata.get(txHashStr);
350
+ const existingMeta = this.#indices.getMetadata(txHashStr);
365
351
 
366
352
  if (existingMeta) {
367
- // Step 2a: Mark existing tx as mined
368
- this.#markAsMined(existingMeta, blockId);
353
+ // Mark existing tx as mined
354
+ this.#indices.markAsMined(existingMeta, blockId);
369
355
  } else {
370
- // Step 2b: Add new mined tx
371
- await this.#addNewMinedTx(tx, blockId);
372
- newlyAdded.push(tx);
356
+ // Add new mined tx (callback emitted by #addTx)
357
+ await this.#addTx(tx, { mined: blockId }, opts);
373
358
  }
359
+ await this.#deletedPool.clearIfMinedHigher(txHashStr, blockId.number);
374
360
  }
375
361
  });
376
-
377
- // Step 3: Emit events for newly added txs
378
- if (newlyAdded.length > 0) {
379
- this.#callbacks.onTxsAdded(newlyAdded, opts);
380
- }
381
362
  }
382
363
 
383
364
  async handleMinedBlock(block: L2Block): Promise<void> {
@@ -392,7 +373,7 @@ export class TxPoolV2Impl {
392
373
  const feePayers: string[] = [];
393
374
  const found: TxMetaData[] = [];
394
375
  for (const txHash of txHashes) {
395
- const meta = this.#metadata.get(txHash.toString());
376
+ const meta = this.#indices.getMetadata(txHash.toString());
396
377
  if (meta) {
397
378
  feePayers.push(meta.feePayer);
398
379
  found.push(meta);
@@ -400,24 +381,26 @@ export class TxPoolV2Impl {
400
381
  }
401
382
 
402
383
  // Step 4: Mark txs as mined (only those we have in the pool)
403
- this.#markTxsAsMined(found, blockId);
384
+ for (const meta of found) {
385
+ this.#indices.markAsMined(meta, blockId);
386
+ await this.#deletedPool.clearIfMinedHigher(meta.txHash, blockId.number);
387
+ }
404
388
 
405
389
  // Step 5: Run eviction rules (remove pending txs with conflicting nullifiers/expired timestamps)
406
390
  await this.#evictionManager.evictAfterNewBlock(block.header, nullifiers, feePayers);
407
391
 
408
- this.#callbacks.onTxsRemoved(txHashes.map(h => h.toBigInt()));
409
392
  this.#log.info(`Marked ${found.length} txs as mined in block ${blockId.number}`);
410
393
  }
411
394
 
412
395
  async prepareForSlot(slotNumber: SlotNumber): Promise<void> {
413
396
  // Step 1: Find expired protected txs
414
- const expiredProtected = this.#findExpiredProtectedTxs(slotNumber);
397
+ const expiredProtected = this.#indices.findExpiredProtectedTxs(slotNumber);
415
398
 
416
399
  // Step 2: Clear protection for all expired entries (including those without metadata)
417
- this.#clearProtection(expiredProtected);
400
+ this.#indices.clearProtection(expiredProtected);
418
401
 
419
402
  // Step 3: Filter to only txs that have metadata and are not mined
420
- const txsToRestore = this.#filterRestorable(expiredProtected);
403
+ const txsToRestore = this.#indices.filterRestorable(expiredProtected);
421
404
  if (txsToRestore.length === 0) {
422
405
  return;
423
406
  }
@@ -425,7 +408,7 @@ export class TxPoolV2Impl {
425
408
  this.#log.info(`Preparing for slot ${slotNumber}: unprotecting ${txsToRestore.length} txs`);
426
409
 
427
410
  // Step 4: Validate for pending pool
428
- const { valid, invalid } = await this.#validateForPending(txsToRestore);
411
+ const { valid, invalid } = await this.#revalidateMetadata(txsToRestore, 'during prepareForSlot');
429
412
 
430
413
  // Step 5: Resolve nullifier conflicts and add winners to pending indices
431
414
  const { added, toEvict } = this.#applyNullifierConflictResolution(valid);
@@ -446,7 +429,7 @@ export class TxPoolV2Impl {
446
429
 
447
430
  async handlePrunedBlocks(latestBlock: L2BlockId): Promise<void> {
448
431
  // Step 1: Find transactions mined after the prune point
449
- const txsToUnmine = this.#findTxsMinedAfter(latestBlock.number);
432
+ const txsToUnmine = this.#indices.findTxsMinedAfter(latestBlock.number);
450
433
  if (txsToUnmine.length === 0) {
451
434
  this.#log.debug(`No transactions to un-mine for prune to block ${latestBlock.number}`);
452
435
  return;
@@ -454,28 +437,40 @@ export class TxPoolV2Impl {
454
437
 
455
438
  this.#log.info(`Handling prune to block ${latestBlock.number}: un-mining ${txsToUnmine.length} txs`);
456
439
 
457
- // Step 2: Unmine - clear mined status from metadata
458
- this.#unmineTxs(txsToUnmine);
440
+ // Step 2: Mark ALL un-mined txs with their original mined block number
441
+ // This ensures they get soft-deleted if removed later, and only hard-deleted
442
+ // when their original mined block is finalized
443
+ await this.#deletedPool.markFromPrunedBlock(
444
+ txsToUnmine.map(m => ({
445
+ txHash: m.txHash,
446
+ minedAtBlock: BlockNumber(m.minedL2BlockId!.number),
447
+ })),
448
+ );
449
+
450
+ // Step 3: Unmine - clear mined status from metadata
451
+ for (const meta of txsToUnmine) {
452
+ this.#indices.markAsUnmined(meta);
453
+ }
459
454
 
460
- // Step 3: Filter out protected txs (they'll be handled by prepareForSlot)
461
- const unprotectedTxs = this.#filterUnprotected(txsToUnmine);
455
+ // Step 4: Filter out protected txs (they'll be handled by prepareForSlot)
456
+ const unprotectedTxs = this.#indices.filterUnprotected(txsToUnmine);
462
457
 
463
458
  // Step 4: Validate for pending pool
464
- const { valid, invalid } = await this.#validateForPending(unprotectedTxs);
459
+ const { valid, invalid } = await this.#revalidateMetadata(unprotectedTxs, 'during handlePrunedBlocks');
465
460
 
466
- // Step 5: Resolve nullifier conflicts and add winners to pending indices
461
+ // Step 6: Resolve nullifier conflicts and add winners to pending indices
467
462
  const { toEvict } = this.#applyNullifierConflictResolution(valid);
468
463
 
469
- // Step 6: Delete invalid and evicted txs
464
+ // Step 7: Delete invalid and evicted txs
470
465
  await this.#deleteTxsBatch([...invalid, ...toEvict]);
471
466
 
472
- // Step 7: Run eviction rules for ALL pending txs (not just restored ones)
467
+ // Step 8: Run eviction rules for ALL pending txs (not just restored ones)
473
468
  // This handles cases like existing pending txs with invalid fee payer balances
474
469
  await this.#evictionManager.evictAfterChainPrune(latestBlock.number);
475
470
  }
476
471
 
477
472
  async handleFailedExecution(txHashes: TxHash[]): Promise<void> {
478
- // Step 1: Delete failed txs
473
+ // Delete failed txs
479
474
  await this.#deleteTxsBatch(txHashes.map(h => h.toString()));
480
475
 
481
476
  this.#log.info(`Deleted ${txHashes.length} failed txs`);
@@ -484,16 +479,13 @@ export class TxPoolV2Impl {
484
479
  async handleFinalizedBlock(block: BlockHeader): Promise<void> {
485
480
  const blockNumber = block.globalVariables.blockNumber;
486
481
 
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
- }
482
+ // Step 1: Find mined txs at or before finalized block
483
+ const minedTxsToFinalize = this.#indices.findTxsMinedAtOrBefore(blockNumber);
492
484
 
493
- // Step 2: Collect txs for archiving (before deletion)
485
+ // Step 2: Collect mined txs for archiving (before deletion)
494
486
  const txsToArchive: Tx[] = [];
495
487
  if (this.#archive.isEnabled()) {
496
- for (const txHashStr of txsToFinalize) {
488
+ for (const txHashStr of minedTxsToFinalize) {
497
489
  const buffer = await this.#txsDB.getAsync(txHashStr);
498
490
  if (buffer) {
499
491
  txsToArchive.push(Tx.fromBuffer(buffer));
@@ -501,15 +493,20 @@ export class TxPoolV2Impl {
501
493
  }
502
494
  }
503
495
 
504
- // Step 3: Delete from active pool
505
- await this.#deleteTxsBatch(txsToFinalize);
496
+ // Step 3: Delete mined txs from active pool
497
+ await this.#deleteTxsBatch(minedTxsToFinalize);
506
498
 
507
- // Step 4: Archive
499
+ // Step 4: Finalize soft-deleted txs
500
+ await this.#deletedPool.finalizeBlock(blockNumber);
501
+
502
+ // Step 5: Archive mined txs
508
503
  if (txsToArchive.length > 0) {
509
504
  await this.#archive.archiveTxs(txsToArchive);
510
505
  }
511
506
 
512
- this.#log.info(`Finalized ${txsToFinalize.length} txs from blocks up to ${blockNumber}`);
507
+ if (minedTxsToFinalize.length > 0) {
508
+ this.#log.info(`Finalized ${minedTxsToFinalize.length} mined txs from blocks up to ${blockNumber}`);
509
+ }
513
510
  }
514
511
 
515
512
  // === Query Methods ===
@@ -529,42 +526,40 @@ export class TxPoolV2Impl {
529
526
  }
530
527
 
531
528
  hasTxs(txHashes: TxHash[]): boolean[] {
532
- return txHashes.map(h => this.#metadata.has(h.toString()));
529
+ return txHashes.map(h => {
530
+ const hashStr = h.toString();
531
+ return this.#indices.has(hashStr) || this.#deletedPool.isSoftDeleted(hashStr);
532
+ });
533
533
  }
534
534
 
535
535
  getTxStatus(txHash: TxHash): TxState | undefined {
536
- const meta = this.#metadata.get(txHash.toString());
537
- if (!meta) {
538
- return undefined;
536
+ const txHashStr = txHash.toString();
537
+ const meta = this.#indices.getMetadata(txHashStr);
538
+ if (meta) {
539
+ return this.#indices.getTxState(meta);
540
+ }
541
+ // Check if soft-deleted
542
+ if (this.#deletedPool.isSoftDeleted(txHashStr)) {
543
+ return 'deleted';
539
544
  }
540
- return this.#getTxState(meta);
545
+ return undefined;
541
546
  }
542
547
 
543
548
  getPendingTxHashes(): TxHash[] {
544
- return [...this.#iteratePendingByPriority('desc')].map(hash => TxHash.fromString(hash));
549
+ return [...this.#indices.iteratePendingByPriority('desc')].map(hash => TxHash.fromString(hash));
545
550
  }
546
551
 
547
552
  getPendingTxCount(): number {
548
- let count = 0;
549
- for (const hashes of this.#pendingByPriority.values()) {
550
- count += hashes.size;
551
- }
552
- return count;
553
+ return this.#indices.getPendingTxCount();
553
554
  }
554
555
 
555
556
  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;
557
+ return this.#indices.getMinedTxs().map(([hash, blockId]) => [TxHash.fromString(hash), blockId]);
563
558
  }
564
559
 
565
560
  getMinedTxCount(): number {
566
561
  let count = 0;
567
- for (const meta of this.#metadata.values()) {
562
+ for (const [, meta] of this.#indices.iterateMetadata()) {
568
563
  if (meta.minedL2BlockId !== undefined) {
569
564
  count++;
570
565
  }
@@ -573,11 +568,11 @@ export class TxPoolV2Impl {
573
568
  }
574
569
 
575
570
  isEmpty(): boolean {
576
- return this.#metadata.size === 0;
571
+ return this.#indices.isEmpty();
577
572
  }
578
573
 
579
574
  getTxCount(): number {
580
- return this.#metadata.size;
575
+ return this.#indices.getTxCount();
581
576
  }
582
577
 
583
578
  getArchivedTxByHash(txHash: TxHash): Promise<Tx | undefined> {
@@ -585,18 +580,7 @@ export class TxPoolV2Impl {
585
580
  }
586
581
 
587
582
  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;
583
+ return this.#indices.getLowestPriorityPending(limit).map(h => TxHash.fromString(h));
600
584
  }
601
585
 
602
586
  // === Configuration ===
@@ -617,159 +601,109 @@ export class TxPoolV2Impl {
617
601
 
618
602
  getPoolReadAccess(): PoolReadAccess {
619
603
  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(),
604
+ getMetadata: (txHash: string) => this.#indices.getMetadata(txHash),
605
+ getTxHashByNullifier: (nullifier: string) => this.#indices.getTxHashByNullifier(nullifier),
606
+ getTxHashesByFeePayer: (feePayer: string) => this.#indices.getTxHashesByFeePayer(feePayer),
607
+ getPendingTxCount: () => this.#indices.getPendingTxCount(),
624
608
  };
625
609
  }
626
610
 
627
611
  // === Metrics ===
628
612
 
629
613
  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 };
614
+ return this.#indices.countTxs();
646
615
  }
647
616
 
648
617
  // ============================================================================
649
- // PRIVATE QUERY IMPLEMENTATIONS
618
+ // PRIVATE HELPERS - Transaction Management
650
619
  // ============================================================================
651
620
 
652
621
  /**
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
622
+ * Adds a new transaction to the pool with the specified state.
623
+ * Emits onTxsAdded callback immediately after DB write.
658
624
  */
659
- #getTxState(meta: TxMetaData): TxState {
660
- if (meta.minedL2BlockId !== undefined) {
661
- return 'mined';
662
- } else if (this.#protectedTransactions.has(meta.txHash)) {
663
- return 'protected';
664
- } else {
665
- return 'pending';
666
- }
667
- }
625
+ async #addTx(
626
+ tx: Tx,
627
+ state: 'pending' | { protected: SlotNumber } | { mined: L2BlockId },
628
+ opts: { source?: string } = {},
629
+ ): Promise<TxMetaData> {
630
+ const txHashStr = tx.getTxHash().toString();
631
+ const meta = await buildTxMetaData(tx);
668
632
 
669
- /**
670
- * Iterates pending transaction hashes in priority order.
671
- * @param order - 'desc' for highest priority first, 'asc' for lowest priority first
672
- */
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
- }
689
- }
633
+ await this.#txsDB.set(txHashStr, tx.toBuffer());
634
+ this.#callbacks.onTxsAdded([tx], opts);
690
635
 
691
- // ============================================================================
692
- // HELPER FUNCTIONS - Pipeline Step Functions
693
- // ============================================================================
636
+ if (state === 'pending') {
637
+ this.#indices.addPending(meta);
638
+ } else if ('protected' in state) {
639
+ this.#indices.addProtected(meta, state.protected);
640
+ } else {
641
+ meta.minedL2BlockId = state.mined;
642
+ this.#indices.addMined(meta);
643
+ }
694
644
 
695
- // --- Finding & Filtering Steps ---
645
+ const stateStr = typeof state === 'string' ? state : Object.keys(state)[0];
646
+ this.#log.verbose(`Added ${stateStr} tx ${txHashStr}`, {
647
+ eventName: 'tx-added-to-pool',
648
+ state: stateStr,
649
+ });
696
650
 
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
- }
704
- }
705
- return result;
651
+ return meta;
706
652
  }
707
653
 
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
- }
715
- }
716
- return result;
654
+ /**
655
+ * Deletes a transaction from both indices and DB.
656
+ * Emits onTxsRemoved callback immediately after DB delete.
657
+ */
658
+ /**
659
+ * Deletes a transaction from the pool.
660
+ * Delegates to DeletedPool which decides soft vs hard delete based on whether
661
+ * the tx is from a pruned block.
662
+ */
663
+ async #deleteTx(txHashStr: string): Promise<void> {
664
+ this.#indices.remove(txHashStr);
665
+ this.#callbacks.onTxsRemoved([txHashStr]);
666
+ await this.#deletedPool.deleteTx(txHashStr);
717
667
  }
718
668
 
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
- }
669
+ /** Deletes a batch of transactions, emitting callbacks individually for each. */
670
+ async #deleteTxsBatch(txHashes: string[]): Promise<void> {
671
+ for (const txHashStr of txHashes) {
672
+ await this.#deleteTx(txHashStr);
726
673
  }
727
- return result;
728
674
  }
729
675
 
730
- /** Filters out transactions that are currently protected */
731
- #filterUnprotected(txs: TxMetaData[]): TxMetaData[] {
732
- return txs.filter(meta => !this.#protectedTransactions.has(meta.txHash));
733
- }
676
+ // ============================================================================
677
+ // PRIVATE HELPERS - Validation & Conflict Resolution
678
+ // ============================================================================
734
679
 
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
- }
680
+ /** Validates transaction metadata, returning true if valid */
681
+ async #validateMeta(meta: TxMetaData, validator?: TxValidator<TxMetaData>, context?: string): Promise<boolean> {
682
+ const txValidator = validator ?? (await this.#createTxValidator());
683
+ const result = await txValidator.validateTx(meta);
684
+ if (result.result !== 'valid') {
685
+ const contextStr = context ? ` ${context}` : '';
686
+ this.#log.info(`Tx ${meta.txHash}${contextStr} failed validation: ${result.reason?.join(', ')}`);
687
+ return false;
743
688
  }
744
- return result;
689
+ return true;
745
690
  }
746
691
 
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[] }> {
692
+ /** Validates metadata directly */
693
+ async #revalidateMetadata(
694
+ metas: TxMetaData[],
695
+ context?: string,
696
+ ): Promise<{ valid: TxMetaData[]; invalid: string[] }> {
751
697
  const valid: TxMetaData[] = [];
752
698
  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') {
699
+ const validator = await this.#createTxValidator();
700
+ for (const meta of metas) {
701
+ if (await this.#validateMeta(meta, validator, context)) {
766
702
  valid.push(meta);
767
703
  } else {
768
- this.#log.info(`Tx ${meta.txHash} failed validation: ${result.reason?.join(', ')}`);
769
704
  invalid.push(meta.txHash);
770
705
  }
771
706
  }
772
-
773
707
  return { valid, invalid };
774
708
  }
775
709
 
@@ -785,8 +719,8 @@ export class TxPoolV2Impl {
785
719
  for (const meta of txs) {
786
720
  const conflict = checkNullifierConflict(
787
721
  meta,
788
- nullifier => this.#nullifierToTxHash.get(nullifier),
789
- txHash => this.#metadata.get(txHash),
722
+ nullifier => this.#indices.getTxHashByNullifier(nullifier),
723
+ txHash => this.#indices.getMetadata(txHash),
790
724
  );
791
725
  if (conflict.shouldIgnore) {
792
726
  // Lower priority than existing - don't add, mark for deletion
@@ -796,13 +730,13 @@ export class TxPoolV2Impl {
796
730
  toEvict.push(...conflict.txHashesToEvict);
797
731
  // Remove evicted from indices immediately for subsequent checks
798
732
  for (const evictHash of conflict.txHashesToEvict) {
799
- const evictMeta = this.#metadata.get(evictHash);
733
+ const evictMeta = this.#indices.getMetadata(evictHash);
800
734
  if (evictMeta) {
801
- this.#removeFromPendingIndices(evictMeta);
735
+ this.#indices.removeFromPendingIndices(evictMeta);
802
736
  }
803
737
  }
804
738
  // Add to pending indices immediately so subsequent txs in the batch see this tx
805
- this.#addToPendingIndices(meta);
739
+ this.#indices.addToPendingIndices(meta);
806
740
  added.push(meta);
807
741
  }
808
742
  }
@@ -810,43 +744,10 @@ export class TxPoolV2Impl {
810
744
  return { added, toEvict };
811
745
  }
812
746
 
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 ---
747
+ // ============================================================================
748
+ // PRIVATE HELPERS - Block & Hydration
749
+ // ============================================================================
848
750
 
849
- /** Builds a block ID from a block header */
850
751
  async #buildBlockId(block: BlockHeader): Promise<L2BlockId> {
851
752
  return {
852
753
  number: block.globalVariables.blockNumber,
@@ -866,50 +767,6 @@ export class TxPoolV2Impl {
866
767
  };
867
768
  }
868
769
 
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
770
  /** Loads all transactions from the database, returning loaded txs and deserialization errors */
914
771
  async #loadAllTxsFromDb(): Promise<{
915
772
  loaded: { tx: Tx; meta: TxMetaData }[];
@@ -919,6 +776,11 @@ export class TxPoolV2Impl {
919
776
  const errors: string[] = [];
920
777
 
921
778
  for await (const [txHashStr, buffer] of this.#txsDB.entriesAsync()) {
779
+ // Skip soft-deleted transactions - they stay in DB but not in indices
780
+ if (this.#deletedPool.isSoftDeleted(txHashStr)) {
781
+ continue;
782
+ }
783
+
922
784
  try {
923
785
  const tx = Tx.fromBuffer(buffer);
924
786
  const meta = await buildTxMetaData(tx);
@@ -949,50 +811,6 @@ export class TxPoolV2Impl {
949
811
  }
950
812
  }
951
813
 
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
814
  /**
997
815
  * Rebuilds the pending pool by processing each tx through pre-add rules.
998
816
  * Starts with an empty pending pool and adds txs one by one, resolving conflicts.
@@ -1016,18 +834,18 @@ export class TxPoolV2Impl {
1016
834
 
1017
835
  // Evict any conflicting txs identified by pre-add rules
1018
836
  for (const evictHashStr of preAddResult.txHashesToEvict) {
1019
- const evictMeta = this.#metadata.get(evictHashStr);
837
+ const evictMeta = this.#indices.getMetadata(evictHashStr);
1020
838
  if (evictMeta) {
1021
- this.#removeFromPendingIndices(evictMeta);
1022
- this.#metadata.delete(evictHashStr);
839
+ this.#indices.removeFromPendingIndices(evictMeta);
840
+ this.#indices.remove(evictHashStr);
1023
841
  rejected.push(evictHashStr);
1024
842
  accepted.delete(evictHashStr);
1025
843
  this.#log.debug(`Evicted tx ${evictHashStr} during rebuild due to conflict with ${meta.txHash}`);
1026
844
  }
1027
845
  }
1028
846
 
1029
- // Add to metadata and pending indices
1030
- this.#addToIndices(meta);
847
+ // Add to indices
848
+ this.#indices.addPending(meta);
1031
849
  accepted.add(meta.txHash);
1032
850
  }
1033
851
 
@@ -1035,207 +853,32 @@ export class TxPoolV2Impl {
1035
853
  return { accepted: [...accepted], rejected };
1036
854
  }
1037
855
 
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
- // ============================================================================
1062
- // HELPER FUNCTIONS - Index Management
1063
- // ============================================================================
1064
-
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
856
  // ============================================================================
1163
- // HELPER FUNCTIONS - Adapters
857
+ // PRIVATE HELPERS - Pool Access Adapters
1164
858
  // ============================================================================
1165
859
 
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
860
  #createPoolOperations(): PoolOperations {
1186
861
  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
- },
862
+ getPendingTxs: () => this.#indices.getPendingTxs(),
863
+ getPendingFeePayers: () => this.#indices.getPendingFeePayers(),
864
+ getFeePayerPendingTxs: (feePayer: string) => this.#indices.getFeePayerPendingTxs(feePayer),
865
+ getPendingTxCount: () => this.#indices.getPendingTxCount(),
866
+ getLowestPriorityPending: (limit: number) => this.#indices.getLowestPriorityPending(limit),
867
+ deleteTxs: (txHashes: string[]) => this.#deleteTxsBatch(txHashes),
1219
868
  };
1220
869
  }
1221
870
 
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
871
  #createPreAddPoolAccess(): PreAddPoolAccess {
1227
872
  return {
1228
- getMetadata: (txHashStr: string): TxMetaData | undefined => {
1229
- const meta = this.#metadata.get(txHashStr);
1230
- if (!meta || this.#getTxState(meta) !== 'pending') {
873
+ getMetadata: (txHashStr: string) => {
874
+ const meta = this.#indices.getMetadata(txHashStr);
875
+ if (!meta || this.#indices.getTxState(meta) !== 'pending') {
1231
876
  return undefined;
1232
877
  }
1233
878
  return meta;
1234
879
  },
1235
- getTxHashByNullifier: (nullifier: string): string | undefined => {
1236
- return this.#nullifierToTxHash.get(nullifier);
1237
- },
1238
- getFeePayerBalance: async (feePayer: string): Promise<bigint> => {
880
+ getTxHashByNullifier: (nullifier: string) => this.#indices.getTxHashByNullifier(nullifier),
881
+ getFeePayerBalance: async (feePayer: string) => {
1239
882
  const db = this.#worldStateSynchronizer.getCommitted();
1240
883
  const publicStateSource = new DatabasePublicStateSource(db);
1241
884
  const balance = await publicStateSource.storageRead(
@@ -1244,22 +887,9 @@ export class TxPoolV2Impl {
1244
887
  );
1245
888
  return balance.toBigInt();
1246
889
  },
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
- },
890
+ getFeePayerPendingTxs: (feePayer: string) => this.#indices.getFeePayerPendingTxs(feePayer),
891
+ getPendingTxCount: () => this.#indices.getPendingTxCount(),
892
+ getLowestPriorityPendingTx: () => this.#indices.getLowestPriorityPendingTx(),
1263
893
  };
1264
894
  }
1265
895
  }