@aztec/p2p 0.0.1-commit.2e2504e2 → 0.0.1-commit.2eb6648a

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 (108) hide show
  1. package/dest/client/factory.d.ts +3 -3
  2. package/dest/client/factory.d.ts.map +1 -1
  3. package/dest/client/factory.js +2 -2
  4. package/dest/client/interface.d.ts +9 -2
  5. package/dest/client/interface.d.ts.map +1 -1
  6. package/dest/client/p2p_client.d.ts +4 -3
  7. package/dest/client/p2p_client.d.ts.map +1 -1
  8. package/dest/client/p2p_client.js +11 -5
  9. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +1 -1
  10. package/dest/config.d.ts +1 -1
  11. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +94 -87
  12. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  13. package/dest/mem_pools/attestation_pool/attestation_pool.js +411 -3
  14. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +2 -2
  15. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  16. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +351 -85
  17. package/dest/mem_pools/attestation_pool/index.d.ts +2 -3
  18. package/dest/mem_pools/attestation_pool/index.d.ts.map +1 -1
  19. package/dest/mem_pools/attestation_pool/index.js +1 -2
  20. package/dest/mem_pools/index.d.ts +2 -2
  21. package/dest/mem_pools/index.d.ts.map +1 -1
  22. package/dest/mem_pools/index.js +1 -1
  23. package/dest/mem_pools/interface.d.ts +3 -3
  24. package/dest/mem_pools/interface.d.ts.map +1 -1
  25. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +5 -1
  26. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
  27. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +1 -1
  28. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
  29. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +2 -1
  30. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +99 -0
  31. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -0
  32. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +332 -0
  33. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +1 -1
  34. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
  35. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +6 -0
  36. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +1 -1
  37. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
  38. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +193 -486
  39. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +3 -3
  40. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
  41. package/dest/services/dummy_service.d.ts +6 -2
  42. package/dest/services/dummy_service.d.ts.map +1 -1
  43. package/dest/services/dummy_service.js +3 -0
  44. package/dest/services/libp2p/libp2p_service.d.ts +74 -33
  45. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  46. package/dest/services/libp2p/libp2p_service.js +299 -228
  47. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +4 -4
  48. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
  49. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +8 -8
  50. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +6 -4
  51. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
  52. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +16 -11
  53. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +15 -10
  54. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
  55. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +12 -11
  56. package/dest/services/service.d.ts +18 -1
  57. package/dest/services/service.d.ts.map +1 -1
  58. package/dest/services/tx_collection/config.d.ts +3 -3
  59. package/dest/services/tx_collection/config.js +3 -3
  60. package/dest/services/tx_collection/fast_tx_collection.d.ts +4 -5
  61. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
  62. package/dest/services/tx_collection/fast_tx_collection.js +10 -14
  63. package/dest/services/tx_collection/index.d.ts +1 -1
  64. package/dest/services/tx_collection/proposal_tx_collector.d.ts +12 -12
  65. package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +1 -1
  66. package/dest/services/tx_collection/proposal_tx_collector.js +4 -5
  67. package/dest/test-helpers/testbench-utils.d.ts +10 -16
  68. package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
  69. package/dest/test-helpers/testbench-utils.js +32 -30
  70. package/dest/testbench/p2p_client_testbench_worker.js +1 -1
  71. package/package.json +14 -14
  72. package/src/client/factory.ts +3 -4
  73. package/src/client/interface.ts +13 -1
  74. package/src/client/p2p_client.ts +20 -8
  75. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +1 -1
  76. package/src/mem_pools/attestation_pool/attestation_pool.ts +444 -90
  77. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +436 -100
  78. package/src/mem_pools/attestation_pool/index.ts +9 -2
  79. package/src/mem_pools/index.ts +1 -1
  80. package/src/mem_pools/interface.ts +2 -2
  81. package/src/mem_pools/tx_pool_v2/README.md +28 -7
  82. package/src/mem_pools/tx_pool_v2/interfaces.ts +2 -0
  83. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +2 -1
  84. package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +417 -0
  85. package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +3 -0
  86. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +185 -568
  87. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +2 -2
  88. package/src/services/dummy_service.ts +6 -0
  89. package/src/services/libp2p/libp2p_service.ts +304 -230
  90. package/src/services/reqresp/batch-tx-requester/README.md +7 -7
  91. package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +11 -11
  92. package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +22 -13
  93. package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +21 -15
  94. package/src/services/service.ts +20 -0
  95. package/src/services/tx_collection/config.ts +6 -6
  96. package/src/services/tx_collection/fast_tx_collection.ts +14 -24
  97. package/src/services/tx_collection/index.ts +1 -1
  98. package/src/services/tx_collection/proposal_tx_collector.ts +12 -14
  99. package/src/test-helpers/testbench-utils.ts +18 -39
  100. package/src/testbench/p2p_client_testbench_worker.ts +1 -1
  101. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +0 -40
  102. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +0 -1
  103. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +0 -218
  104. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +0 -31
  105. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +0 -1
  106. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +0 -180
  107. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +0 -320
  108. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +0 -264
@@ -6,7 +6,8 @@ import { Tx, TxHash } from '@aztec/stdlib/tx';
6
6
  import { TxArchive } from './archive/index.js';
7
7
  import { EvictionManager, FeePayerBalanceEvictionRule, FeePayerBalancePreAddRule, InvalidTxsAfterMiningRule, InvalidTxsAfterReorgRule, LowPriorityEvictionRule, LowPriorityPreAddRule, NullifierConflictRule } from './eviction/index.js';
8
8
  import { DEFAULT_TX_POOL_V2_CONFIG } from './interfaces.js';
9
- import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } from './tx_metadata.js';
9
+ import { buildTxMetaData, checkNullifierConflict } from './tx_metadata.js';
10
+ import { TxPoolIndices } from './tx_pool_indices.js';
10
11
  /**
11
12
  * Implementation of TxPoolV2 logic.
12
13
  *
@@ -20,14 +21,7 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
20
21
  #worldStateSynchronizer;
21
22
  #pendingTxValidator;
22
23
  // === 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();
24
+ #indices = new TxPoolIndices();
31
25
  // === Config & Services ===
32
26
  #config;
33
27
  #archive;
@@ -80,15 +74,25 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
80
74
  // Step 2: Check mined status for each tx
81
75
  await this.#markMinedStatusBatch(loaded.map((l)=>l.meta));
82
76
  // Step 3: Partition by mined status
83
- const { mined, nonMined } = this.#partitionByMinedStatus(loaded);
77
+ const mined = [];
78
+ const nonMined = [];
79
+ for (const entry of loaded){
80
+ if (entry.meta.minedL2BlockId !== undefined) {
81
+ mined.push(entry.meta);
82
+ } else {
83
+ nonMined.push(entry);
84
+ }
85
+ }
84
86
  // Step 4: Validate non-mined transactions
85
- const { valid, invalid } = await this.#validateNonMinedTxs(nonMined);
87
+ const { valid, invalid } = await this.#validateTxBatch(nonMined, 'on startup');
86
88
  // Step 5: Populate mined indices (these don't need conflict resolution)
87
- this.#populateMinedIndices(mined);
89
+ for (const meta of mined){
90
+ this.#indices.addMined(meta);
91
+ }
88
92
  // Step 6: Rebuild pending pool by running pre-add rules for each tx
89
93
  // This resolves nullifier conflicts, fee payer balance issues, and pool size limits
90
94
  const { rejected } = await this.#rebuildPendingPool(valid);
91
- // Step 7: Delete invalid and rejected txs from DB
95
+ // Step 7: Delete invalid and rejected txs from DB only (indices were never populated for these)
92
96
  const toDelete = [
93
97
  ...deserializationErrors,
94
98
  ...invalid,
@@ -108,7 +112,6 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
108
112
  const accepted = [];
109
113
  const ignored = [];
110
114
  const rejected = [];
111
- const newlyAdded = [];
112
115
  const acceptedPending = new Set();
113
116
  const poolAccess = this.#createPreAddPoolAccess();
114
117
  await this.#store.transactionAsync(async ()=>{
@@ -116,29 +119,30 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
116
119
  const txHash = tx.getTxHash();
117
120
  const txHashStr = txHash.toString();
118
121
  // Skip duplicates
119
- if (this.#isDuplicateTx(txHashStr)) {
122
+ if (this.#indices.has(txHashStr)) {
120
123
  ignored.push(txHash);
121
124
  continue;
122
125
  }
123
126
  // Check mined status first (applies to all paths)
124
127
  const minedBlockId = await this.#getMinedBlockId(txHash);
125
- const preProtectedSlot = this.#protectedTransactions.get(txHashStr);
128
+ const preProtectedSlot = this.#indices.getProtectionSlot(txHashStr);
126
129
  if (minedBlockId) {
127
130
  // Already mined - add directly (protection already set if pre-protected)
128
- await this.#addNewMinedTx(tx, minedBlockId);
131
+ await this.#addTx(tx, {
132
+ mined: minedBlockId
133
+ }, opts);
129
134
  accepted.push(txHash);
130
- newlyAdded.push(tx);
131
135
  } else if (preProtectedSlot !== undefined) {
132
136
  // Pre-protected and not mined - add as protected (bypass validation)
133
- await this.#addNewProtectedTx(tx, preProtectedSlot);
137
+ await this.#addTx(tx, {
138
+ protected: preProtectedSlot
139
+ }, opts);
134
140
  accepted.push(txHash);
135
- newlyAdded.push(tx);
136
141
  } else {
137
142
  // Regular pending tx - validate and run pre-add rules
138
- const result = await this.#tryAddRegularPendingTx(tx, poolAccess, acceptedPending, ignored);
143
+ const result = await this.#tryAddRegularPendingTx(tx, opts, poolAccess, acceptedPending, ignored);
139
144
  if (result.status === 'accepted') {
140
145
  acceptedPending.add(txHashStr);
141
- newlyAdded.push(tx);
142
146
  } else if (result.status === 'rejected') {
143
147
  rejected.push(txHash);
144
148
  } else {
@@ -153,29 +157,23 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
153
157
  }
154
158
  // Run post-add eviction rules for pending txs
155
159
  if (acceptedPending.size > 0) {
156
- const feePayers = Array.from(acceptedPending).map((txHash)=>this.#metadata.get(txHash).feePayer);
160
+ const feePayers = Array.from(acceptedPending).map((txHash)=>this.#indices.getMetadata(txHash).feePayer);
157
161
  const uniqueFeePayers = new Set(feePayers);
158
162
  await this.#evictionManager.evictAfterNewTxs(Array.from(acceptedPending), [
159
163
  ...uniqueFeePayers
160
164
  ]);
161
165
  }
162
- // Emit events
163
- if (newlyAdded.length > 0) {
164
- this.#callbacks.onTxsAdded(newlyAdded, opts);
165
- }
166
166
  return {
167
167
  accepted,
168
168
  ignored,
169
169
  rejected
170
170
  };
171
171
  }
172
- /** Validates and adds a regular pending tx. Returns status. */ async #tryAddRegularPendingTx(tx, poolAccess, acceptedPending, ignored) {
172
+ /** Validates and adds a regular pending tx. Returns status. */ async #tryAddRegularPendingTx(tx, opts, poolAccess, acceptedPending, ignored) {
173
173
  const txHash = tx.getTxHash();
174
174
  const txHashStr = txHash.toString();
175
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(', ')}`);
176
+ if (!await this.#validateTx(tx)) {
179
177
  return {
180
178
  status: 'rejected'
181
179
  };
@@ -189,17 +187,18 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
189
187
  status: 'ignored'
190
188
  };
191
189
  }
192
- // Evict conflicts (tracking intra-batch evictions)
190
+ // Evict conflicts
193
191
  for (const evictHashStr of preAddResult.txHashesToEvict){
194
192
  await this.#deleteTx(evictHashStr);
195
193
  this.#log.debug(`Evicted tx ${evictHashStr} due to higher-fee tx ${txHashStr}`);
196
194
  if (acceptedPending.has(evictHashStr)) {
195
+ // Evicted tx was from this batch - mark as ignored in result
197
196
  acceptedPending.delete(evictHashStr);
198
197
  ignored.push(TxHash.fromString(evictHashStr));
199
198
  }
200
199
  }
201
200
  // Add the transaction
202
- await this.#addNewPendingTx(tx);
201
+ await this.#addTx(tx, 'pending', opts);
203
202
  return {
204
203
  status: 'accepted'
205
204
  };
@@ -207,10 +206,10 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
207
206
  async canAddPendingTx(tx) {
208
207
  const txHashStr = tx.getTxHash().toString();
209
208
  // Check if already in pool
210
- if (this.#metadata.has(txHashStr)) {
209
+ if (this.#indices.has(txHashStr)) {
211
210
  return 'ignored';
212
211
  }
213
- // Validate transaction
212
+ // Validate transaction (no logging for dry-run check)
214
213
  const validationResult = await this.#pendingTxValidator.validateTx(tx);
215
214
  if (validationResult.result !== 'valid') {
216
215
  return 'rejected';
@@ -223,46 +222,46 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
223
222
  }
224
223
  async addProtectedTxs(txs, block, opts) {
225
224
  const slotNumber = block.globalVariables.slotNumber;
226
- const newlyAdded = [];
227
225
  await this.#store.transactionAsync(async ()=>{
228
226
  for (const tx of txs){
229
227
  const txHash = tx.getTxHash();
230
228
  const txHashStr = txHash.toString();
231
- const isNew = !this.#metadata.has(txHashStr);
229
+ const isNew = !this.#indices.has(txHashStr);
232
230
  const minedBlockId = await this.#getMinedBlockId(txHash);
233
231
  if (isNew) {
234
- // New tx - add as mined or protected
232
+ // New tx - add as mined or protected (callback emitted by #addTx)
235
233
  if (minedBlockId) {
236
- await this.#addNewMinedTx(tx, minedBlockId);
237
- this.#protectedTransactions.set(txHashStr, slotNumber);
234
+ await this.#addTx(tx, {
235
+ mined: minedBlockId
236
+ }, opts);
237
+ this.#indices.setProtection(txHashStr, slotNumber);
238
238
  } else {
239
- await this.#addNewProtectedTx(tx, slotNumber);
239
+ await this.#addTx(tx, {
240
+ protected: slotNumber
241
+ }, opts);
240
242
  }
241
- newlyAdded.push(tx);
242
243
  } else {
243
244
  // Existing tx - update protection and mined status
244
- this.#updateProtection(txHashStr, slotNumber);
245
+ this.#indices.updateProtection(txHashStr, slotNumber);
245
246
  if (minedBlockId) {
246
- this.#markAsMined(this.#metadata.get(txHashStr), minedBlockId);
247
+ const meta = this.#indices.getMetadata(txHashStr);
248
+ this.#indices.markAsMined(meta, minedBlockId);
247
249
  }
248
250
  }
249
251
  }
250
252
  });
251
- if (newlyAdded.length > 0) {
252
- this.#callbacks.onTxsAdded(newlyAdded, opts);
253
- }
254
253
  }
255
254
  protectTxs(txHashes, block) {
256
255
  const slotNumber = block.globalVariables.slotNumber;
257
256
  const missing = [];
258
257
  for (const txHash of txHashes){
259
258
  const txHashStr = txHash.toString();
260
- if (this.#metadata.has(txHashStr)) {
261
- // Step 1a: Update protection for existing tx
262
- this.#updateProtection(txHashStr, slotNumber);
259
+ if (this.#indices.has(txHashStr)) {
260
+ // Update protection for existing tx
261
+ this.#indices.updateProtection(txHashStr, slotNumber);
263
262
  } else {
264
- // Step 1b: Pre-record protection for tx we don't have yet
265
- this.#protectedTransactions.set(txHashStr, slotNumber);
263
+ // Pre-record protection for tx we don't have yet
264
+ this.#indices.setProtection(txHashStr, slotNumber);
266
265
  missing.push(txHash);
267
266
  }
268
267
  }
@@ -271,25 +270,21 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
271
270
  async addMinedTxs(txs, block, opts) {
272
271
  // Step 1: Build block ID
273
272
  const blockId = await this.#buildBlockId(block);
274
- const newlyAdded = [];
275
273
  await this.#store.transactionAsync(async ()=>{
276
274
  for (const tx of txs){
277
275
  const txHashStr = tx.getTxHash().toString();
278
- const existingMeta = this.#metadata.get(txHashStr);
276
+ const existingMeta = this.#indices.getMetadata(txHashStr);
279
277
  if (existingMeta) {
280
- // Step 2a: Mark existing tx as mined
281
- this.#markAsMined(existingMeta, blockId);
278
+ // Mark existing tx as mined
279
+ this.#indices.markAsMined(existingMeta, blockId);
282
280
  } else {
283
- // Step 2b: Add new mined tx
284
- await this.#addNewMinedTx(tx, blockId);
285
- newlyAdded.push(tx);
281
+ // Add new mined tx (callback emitted by #addTx)
282
+ await this.#addTx(tx, {
283
+ mined: blockId
284
+ }, opts);
286
285
  }
287
286
  }
288
287
  });
289
- // Step 3: Emit events for newly added txs
290
- if (newlyAdded.length > 0) {
291
- this.#callbacks.onTxsAdded(newlyAdded, opts);
292
- }
293
288
  }
294
289
  async handleMinedBlock(block) {
295
290
  // Step 1: Build block ID
@@ -301,32 +296,33 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
301
296
  const feePayers = [];
302
297
  const found = [];
303
298
  for (const txHash of txHashes){
304
- const meta = this.#metadata.get(txHash.toString());
299
+ const meta = this.#indices.getMetadata(txHash.toString());
305
300
  if (meta) {
306
301
  feePayers.push(meta.feePayer);
307
302
  found.push(meta);
308
303
  }
309
304
  }
310
305
  // Step 4: Mark txs as mined (only those we have in the pool)
311
- this.#markTxsAsMined(found, blockId);
306
+ for (const meta of found){
307
+ this.#indices.markAsMined(meta, blockId);
308
+ }
312
309
  // Step 5: Run eviction rules (remove pending txs with conflicting nullifiers/expired timestamps)
313
310
  await this.#evictionManager.evictAfterNewBlock(block.header, nullifiers, feePayers);
314
- this.#callbacks.onTxsRemoved(txHashes.map((h)=>h.toBigInt()));
315
311
  this.#log.info(`Marked ${found.length} txs as mined in block ${blockId.number}`);
316
312
  }
317
313
  async prepareForSlot(slotNumber) {
318
314
  // Step 1: Find expired protected txs
319
- const expiredProtected = this.#findExpiredProtectedTxs(slotNumber);
315
+ const expiredProtected = this.#indices.findExpiredProtectedTxs(slotNumber);
320
316
  // Step 2: Clear protection for all expired entries (including those without metadata)
321
- this.#clearProtection(expiredProtected);
317
+ this.#indices.clearProtection(expiredProtected);
322
318
  // Step 3: Filter to only txs that have metadata and are not mined
323
- const txsToRestore = this.#filterRestorable(expiredProtected);
319
+ const txsToRestore = this.#indices.filterRestorable(expiredProtected);
324
320
  if (txsToRestore.length === 0) {
325
321
  return;
326
322
  }
327
323
  this.#log.info(`Preparing for slot ${slotNumber}: unprotecting ${txsToRestore.length} txs`);
328
324
  // Step 4: Validate for pending pool
329
- const { valid, invalid } = await this.#validateForPending(txsToRestore);
325
+ const { valid, invalid } = await this.#loadAndValidateTxs(txsToRestore, 'during prepareForSlot');
330
326
  // Step 5: Resolve nullifier conflicts and add winners to pending indices
331
327
  const { added, toEvict } = this.#applyNullifierConflictResolution(valid);
332
328
  // Step 6: Delete invalid and evicted txs
@@ -345,18 +341,20 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
345
341
  }
346
342
  async handlePrunedBlocks(latestBlock) {
347
343
  // Step 1: Find transactions mined after the prune point
348
- const txsToUnmine = this.#findTxsMinedAfter(latestBlock.number);
344
+ const txsToUnmine = this.#indices.findTxsMinedAfter(latestBlock.number);
349
345
  if (txsToUnmine.length === 0) {
350
346
  this.#log.debug(`No transactions to un-mine for prune to block ${latestBlock.number}`);
351
347
  return;
352
348
  }
353
349
  this.#log.info(`Handling prune to block ${latestBlock.number}: un-mining ${txsToUnmine.length} txs`);
354
350
  // Step 2: Unmine - clear mined status from metadata
355
- this.#unmineTxs(txsToUnmine);
351
+ for (const meta of txsToUnmine){
352
+ this.#indices.markAsUnmined(meta);
353
+ }
356
354
  // Step 3: Filter out protected txs (they'll be handled by prepareForSlot)
357
- const unprotectedTxs = this.#filterUnprotected(txsToUnmine);
355
+ const unprotectedTxs = this.#indices.filterUnprotected(txsToUnmine);
358
356
  // Step 4: Validate for pending pool
359
- const { valid, invalid } = await this.#validateForPending(unprotectedTxs);
357
+ const { valid, invalid } = await this.#loadAndValidateTxs(unprotectedTxs, 'during handlePrunedBlocks');
360
358
  // Step 5: Resolve nullifier conflicts and add winners to pending indices
361
359
  const { toEvict } = this.#applyNullifierConflictResolution(valid);
362
360
  // Step 6: Delete invalid and evicted txs
@@ -369,14 +367,14 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
369
367
  await this.#evictionManager.evictAfterChainPrune(latestBlock.number);
370
368
  }
371
369
  async handleFailedExecution(txHashes) {
372
- // Step 1: Delete failed txs
370
+ // Delete failed txs
373
371
  await this.#deleteTxsBatch(txHashes.map((h)=>h.toString()));
374
372
  this.#log.info(`Deleted ${txHashes.length} failed txs`);
375
373
  }
376
374
  async handleFinalizedBlock(block) {
377
375
  const blockNumber = block.globalVariables.blockNumber;
378
376
  // Step 1: Find txs mined at or before finalized block
379
- const txsToFinalize = this.#findTxsMinedAtOrBefore(blockNumber);
377
+ const txsToFinalize = this.#indices.findTxsMinedAtOrBefore(blockNumber);
380
378
  if (txsToFinalize.length === 0) {
381
379
  return;
382
380
  }
@@ -412,42 +410,32 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
412
410
  return results;
413
411
  }
414
412
  hasTxs(txHashes) {
415
- return txHashes.map((h)=>this.#metadata.has(h.toString()));
413
+ return txHashes.map((h)=>this.#indices.has(h.toString()));
416
414
  }
417
415
  getTxStatus(txHash) {
418
- const meta = this.#metadata.get(txHash.toString());
416
+ const meta = this.#indices.getMetadata(txHash.toString());
419
417
  if (!meta) {
420
418
  return undefined;
421
419
  }
422
- return this.#getTxState(meta);
420
+ return this.#indices.getTxState(meta);
423
421
  }
424
422
  getPendingTxHashes() {
425
423
  return [
426
- ...this.#iteratePendingByPriority('desc')
424
+ ...this.#indices.iteratePendingByPriority('desc')
427
425
  ].map((hash)=>TxHash.fromString(hash));
428
426
  }
429
427
  getPendingTxCount() {
430
- let count = 0;
431
- for (const hashes of this.#pendingByPriority.values()){
432
- count += hashes.size;
433
- }
434
- return count;
428
+ return this.#indices.getPendingTxCount();
435
429
  }
436
430
  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;
431
+ return this.#indices.getMinedTxs().map(([hash, blockId])=>[
432
+ TxHash.fromString(hash),
433
+ blockId
434
+ ]);
447
435
  }
448
436
  getMinedTxCount() {
449
437
  let count = 0;
450
- for (const meta of this.#metadata.values()){
438
+ for (const [, meta] of this.#indices.iterateMetadata()){
451
439
  if (meta.minedL2BlockId !== undefined) {
452
440
  count++;
453
441
  }
@@ -455,26 +443,16 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
455
443
  return count;
456
444
  }
457
445
  isEmpty() {
458
- return this.#metadata.size === 0;
446
+ return this.#indices.isEmpty();
459
447
  }
460
448
  getTxCount() {
461
- return this.#metadata.size;
449
+ return this.#indices.getTxCount();
462
450
  }
463
451
  getArchivedTxByHash(txHash) {
464
452
  return this.#archive.getTxByHash(txHash);
465
453
  }
466
454
  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;
455
+ return this.#indices.getLowestPriorityPending(limit).map((h)=>TxHash.fromString(h));
478
456
  }
479
457
  // === Configuration ===
480
458
  updateConfig(config) {
@@ -491,132 +469,98 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
491
469
  // === Pool Read Access ===
492
470
  getPoolReadAccess() {
493
471
  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()
472
+ getMetadata: (txHash)=>this.#indices.getMetadata(txHash),
473
+ getTxHashByNullifier: (nullifier)=>this.#indices.getTxHashByNullifier(nullifier),
474
+ getTxHashesByFeePayer: (feePayer)=>this.#indices.getTxHashesByFeePayer(feePayer),
475
+ getPendingTxCount: ()=>this.#indices.getPendingTxCount()
498
476
  };
499
477
  }
500
478
  // === Metrics ===
501
479
  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
- };
480
+ return this.#indices.countTxs();
520
481
  }
521
482
  // ============================================================================
522
- // PRIVATE QUERY IMPLEMENTATIONS
483
+ // PRIVATE HELPERS - Transaction Management
523
484
  // ============================================================================
524
485
  /**
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';
486
+ * Adds a new transaction to the pool with the specified state.
487
+ * Emits onTxsAdded callback immediately after DB write.
488
+ */ async #addTx(tx, state, opts = {}) {
489
+ const txHashStr = tx.getTxHash().toString();
490
+ const meta = await buildTxMetaData(tx);
491
+ await this.#txsDB.set(txHashStr, tx.toBuffer());
492
+ this.#callbacks.onTxsAdded([
493
+ tx
494
+ ], opts);
495
+ if (state === 'pending') {
496
+ this.#indices.addPending(meta);
497
+ } else if ('protected' in state) {
498
+ this.#indices.addProtected(meta, state.protected);
535
499
  } else {
536
- return 'pending';
500
+ meta.minedL2BlockId = state.mined;
501
+ this.#indices.addMined(meta);
537
502
  }
503
+ const stateStr = typeof state === 'string' ? state : Object.keys(state)[0];
504
+ this.#log.verbose(`Added ${stateStr} tx ${txHashStr}`, {
505
+ eventName: 'tx-added-to-pool',
506
+ state: stateStr
507
+ });
508
+ return meta;
538
509
  }
539
510
  /**
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
- }
511
+ * Deletes a transaction from both indices and DB.
512
+ * Emits onTxsRemoved callback immediately after DB delete.
513
+ */ async #deleteTx(txHashStr) {
514
+ this.#indices.remove(txHashStr);
515
+ await this.#txsDB.delete(txHashStr);
516
+ this.#callbacks.onTxsRemoved([
517
+ txHashStr
518
+ ]);
519
+ }
520
+ /** Deletes a batch of transactions, emitting callbacks individually for each. */ async #deleteTxsBatch(txHashes) {
521
+ for (const txHashStr of txHashes){
522
+ await this.#deleteTx(txHashStr);
557
523
  }
558
524
  }
559
525
  // ============================================================================
560
- // HELPER FUNCTIONS - Pipeline Step Functions
526
+ // PRIVATE HELPERS - Validation & Conflict Resolution
561
527
  // ============================================================================
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
- }
528
+ /** Validates a single transaction, returning true if valid */ async #validateTx(tx, context) {
529
+ const result = await this.#pendingTxValidator.validateTx(tx);
530
+ if (result.result !== 'valid') {
531
+ const contextStr = context ? ` ${context}` : '';
532
+ this.#log.info(`Tx ${tx.getTxHash()}${contextStr} failed validation: ${result.reason?.join(', ')}`);
533
+ return false;
569
534
  }
570
- return result;
535
+ return true;
571
536
  }
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);
537
+ /** Loads transactions from DB, returning loaded txs and missing hashes */ async #loadTxsFromDb(metas) {
538
+ const loaded = [];
539
+ const missing = [];
540
+ for (const meta of metas){
541
+ const buffer = await this.#txsDB.getAsync(meta.txHash);
542
+ if (!buffer) {
543
+ this.#log.warn(`Tx ${meta.txHash} not found in DB`);
544
+ missing.push(meta.txHash);
545
+ continue;
599
546
  }
547
+ loaded.push({
548
+ tx: Tx.fromBuffer(buffer),
549
+ meta
550
+ });
600
551
  }
601
- return result;
552
+ return {
553
+ loaded,
554
+ missing
555
+ };
602
556
  }
603
- // --- Validation & Conflict Resolution Steps ---
604
- /** Validates transactions for pending pool, returning valid and invalid groups */ async #validateForPending(txs) {
557
+ /** Validates a batch of transactions, returning valid and invalid groups */ async #validateTxBatch(txs, context) {
605
558
  const valid = [];
606
559
  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') {
560
+ for (const { tx, meta } of txs){
561
+ if (await this.#validateTx(tx, context)) {
617
562
  valid.push(meta);
618
563
  } else {
619
- this.#log.info(`Tx ${meta.txHash} failed validation: ${result.reason?.join(', ')}`);
620
564
  invalid.push(meta.txHash);
621
565
  }
622
566
  }
@@ -625,6 +569,17 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
625
569
  invalid
626
570
  };
627
571
  }
572
+ /** Loads transactions from DB and validates them */ async #loadAndValidateTxs(metas, context) {
573
+ const { loaded, missing } = await this.#loadTxsFromDb(metas);
574
+ const { valid, invalid } = await this.#validateTxBatch(loaded, context);
575
+ return {
576
+ valid,
577
+ invalid: [
578
+ ...missing,
579
+ ...invalid
580
+ ]
581
+ };
582
+ }
628
583
  /**
629
584
  * Resolves nullifier conflicts between incoming txs and existing pending txs.
630
585
  * Modifies the pending indices during iteration to maintain consistent state
@@ -633,7 +588,7 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
633
588
  const added = [];
634
589
  const toEvict = [];
635
590
  for (const meta of txs){
636
- const conflict = checkNullifierConflict(meta, (nullifier)=>this.#nullifierToTxHash.get(nullifier), (txHash)=>this.#metadata.get(txHash));
591
+ const conflict = checkNullifierConflict(meta, (nullifier)=>this.#indices.getTxHashByNullifier(nullifier), (txHash)=>this.#indices.getMetadata(txHash));
637
592
  if (conflict.shouldIgnore) {
638
593
  // Lower priority than existing - don't add, mark for deletion
639
594
  toEvict.push(meta.txHash);
@@ -642,13 +597,13 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
642
597
  toEvict.push(...conflict.txHashesToEvict);
643
598
  // Remove evicted from indices immediately for subsequent checks
644
599
  for (const evictHash of conflict.txHashesToEvict){
645
- const evictMeta = this.#metadata.get(evictHash);
600
+ const evictMeta = this.#indices.getMetadata(evictHash);
646
601
  if (evictMeta) {
647
- this.#removeFromPendingIndices(evictMeta);
602
+ this.#indices.removeFromPendingIndices(evictMeta);
648
603
  }
649
604
  }
650
605
  // Add to pending indices immediately so subsequent txs in the batch see this tx
651
- this.#addToPendingIndices(meta);
606
+ this.#indices.addToPendingIndices(meta);
652
607
  added.push(meta);
653
608
  }
654
609
  }
@@ -657,32 +612,10 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
657
612
  toEvict
658
613
  };
659
614
  }
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) {
615
+ // ============================================================================
616
+ // PRIVATE HELPERS - Block & Hydration
617
+ // ============================================================================
618
+ async #buildBlockId(block) {
686
619
  return {
687
620
  number: block.globalVariables.blockNumber,
688
621
  hash: (await block.hash()).toString()
@@ -698,36 +631,6 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
698
631
  hash: txEffect.l2BlockHash.toString()
699
632
  };
700
633
  }
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
634
  /** Loads all transactions from the database, returning loaded txs and deserialization errors */ async #loadAllTxsFromDb() {
732
635
  const loaded = [];
733
636
  const errors = [];
@@ -768,43 +671,6 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
768
671
  }
769
672
  }
770
673
  }
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
674
  /**
809
675
  * Rebuilds the pending pool by processing each tx through pre-add rules.
810
676
  * Starts with an empty pending pool and adds txs one by one, resolving conflicts.
@@ -824,17 +690,17 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
824
690
  }
825
691
  // Evict any conflicting txs identified by pre-add rules
826
692
  for (const evictHashStr of preAddResult.txHashesToEvict){
827
- const evictMeta = this.#metadata.get(evictHashStr);
693
+ const evictMeta = this.#indices.getMetadata(evictHashStr);
828
694
  if (evictMeta) {
829
- this.#removeFromPendingIndices(evictMeta);
830
- this.#metadata.delete(evictHashStr);
695
+ this.#indices.removeFromPendingIndices(evictMeta);
696
+ this.#indices.remove(evictHashStr);
831
697
  rejected.push(evictHashStr);
832
698
  accepted.delete(evictHashStr);
833
699
  this.#log.debug(`Evicted tx ${evictHashStr} during rebuild due to conflict with ${meta.txHash}`);
834
700
  }
835
701
  }
836
- // Add to metadata and pending indices
837
- this.#addToIndices(meta);
702
+ // Add to indices
703
+ this.#indices.addPending(meta);
838
704
  accepted.add(meta.txHash);
839
705
  }
840
706
  this.#log.info(`Rebuilt pending pool: ${accepted.size} accepted, ${rejected.length} rejected`);
@@ -845,197 +711,38 @@ import { buildTxMetaData, checkNullifierConflict, compareFee, compareTxHash } fr
845
711
  rejected
846
712
  };
847
713
  }
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
714
  // ============================================================================
864
- // HELPER FUNCTIONS - Index Management
715
+ // PRIVATE HELPERS - Pool Access Adapters
865
716
  // ============================================================================
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
- // ============================================================================
948
- // HELPER FUNCTIONS - Adapters
949
- // ============================================================================
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() {
717
+ #createPoolOperations() {
967
718
  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
- }
719
+ getPendingTxs: ()=>this.#indices.getPendingTxs(),
720
+ getPendingFeePayers: ()=>this.#indices.getPendingFeePayers(),
721
+ getFeePayerPendingTxs: (feePayer)=>this.#indices.getFeePayerPendingTxs(feePayer),
722
+ getPendingTxCount: ()=>this.#indices.getPendingTxCount(),
723
+ getLowestPriorityPending: (limit)=>this.#indices.getLowestPriorityPending(limit),
724
+ deleteTxs: (txHashes)=>this.#deleteTxsBatch(txHashes)
1000
725
  };
1001
726
  }
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() {
727
+ #createPreAddPoolAccess() {
1006
728
  return {
1007
729
  getMetadata: (txHashStr)=>{
1008
- const meta = this.#metadata.get(txHashStr);
1009
- if (!meta || this.#getTxState(meta) !== 'pending') {
730
+ const meta = this.#indices.getMetadata(txHashStr);
731
+ if (!meta || this.#indices.getTxState(meta) !== 'pending') {
1010
732
  return undefined;
1011
733
  }
1012
734
  return meta;
1013
735
  },
1014
- getTxHashByNullifier: (nullifier)=>{
1015
- return this.#nullifierToTxHash.get(nullifier);
1016
- },
736
+ getTxHashByNullifier: (nullifier)=>this.#indices.getTxHashByNullifier(nullifier),
1017
737
  getFeePayerBalance: async (feePayer)=>{
1018
738
  const db = this.#worldStateSynchronizer.getCommitted();
1019
739
  const publicStateSource = new DatabasePublicStateSource(db);
1020
740
  const balance = await publicStateSource.storageRead(ProtocolContractAddress.FeeJuice, await computeFeePayerBalanceStorageSlot(AztecAddress.fromString(feePayer)));
1021
741
  return balance.toBigInt();
1022
742
  },
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
- }
743
+ getFeePayerPendingTxs: (feePayer)=>this.#indices.getFeePayerPendingTxs(feePayer),
744
+ getPendingTxCount: ()=>this.#indices.getPendingTxCount(),
745
+ getLowestPriorityPendingTx: ()=>this.#indices.getLowestPriorityPendingTx()
1039
746
  };
1040
747
  }
1041
748
  }