@aztec/p2p 0.84.0 → 0.85.0

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 (79) hide show
  1. package/dest/bootstrap/bootstrap.js +1 -1
  2. package/dest/client/factory.d.ts.map +1 -1
  3. package/dest/client/factory.js +5 -2
  4. package/dest/client/p2p_client.d.ts +2 -0
  5. package/dest/client/p2p_client.d.ts.map +1 -1
  6. package/dest/client/p2p_client.js +4 -1
  7. package/dest/config.d.ts +15 -3
  8. package/dest/config.d.ts.map +1 -1
  9. package/dest/config.js +12 -1
  10. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +56 -2
  11. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
  12. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +229 -16
  13. package/dest/msg_validators/tx_validator/archive_cache.d.ts +14 -0
  14. package/dest/msg_validators/tx_validator/archive_cache.d.ts.map +1 -0
  15. package/dest/msg_validators/tx_validator/archive_cache.js +22 -0
  16. package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
  17. package/dest/msg_validators/tx_validator/block_header_validator.js +2 -2
  18. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
  19. package/dest/msg_validators/tx_validator/data_validator.js +8 -8
  20. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
  21. package/dest/msg_validators/tx_validator/double_spend_validator.js +3 -3
  22. package/dest/msg_validators/tx_validator/gas_validator.d.ts +2 -1
  23. package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
  24. package/dest/msg_validators/tx_validator/gas_validator.js +32 -5
  25. package/dest/msg_validators/tx_validator/index.d.ts +1 -0
  26. package/dest/msg_validators/tx_validator/index.d.ts.map +1 -1
  27. package/dest/msg_validators/tx_validator/index.js +1 -0
  28. package/dest/msg_validators/tx_validator/metadata_validator.d.ts.map +1 -1
  29. package/dest/msg_validators/tx_validator/metadata_validator.js +4 -4
  30. package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
  31. package/dest/msg_validators/tx_validator/phases_validator.js +10 -2
  32. package/dest/msg_validators/tx_validator/tx_proof_validator.d.ts.map +1 -1
  33. package/dest/msg_validators/tx_validator/tx_proof_validator.js +2 -2
  34. package/dest/services/discv5/discV5_service.d.ts +1 -2
  35. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  36. package/dest/services/discv5/discV5_service.js +6 -8
  37. package/dest/services/dummy_service.d.ts +2 -1
  38. package/dest/services/dummy_service.d.ts.map +1 -1
  39. package/dest/services/dummy_service.js +1 -1
  40. package/dest/services/libp2p/libp2p_service.d.ts +14 -8
  41. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  42. package/dest/services/libp2p/libp2p_service.js +2 -2
  43. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  44. package/dest/services/peer-manager/peer_manager.js +0 -1
  45. package/dest/services/service.d.ts +1 -1
  46. package/dest/services/service.d.ts.map +1 -1
  47. package/dest/testbench/p2p_client_testbench_worker.js +46 -16
  48. package/dest/testbench/parse_log_file.js +4 -4
  49. package/dest/testbench/testbench.js +1 -1
  50. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  51. package/dest/testbench/worker_client_manager.js +3 -2
  52. package/dest/util.d.ts +7 -3
  53. package/dest/util.d.ts.map +1 -1
  54. package/dest/util.js +44 -7
  55. package/package.json +12 -12
  56. package/src/bootstrap/bootstrap.ts +1 -1
  57. package/src/client/factory.ts +7 -2
  58. package/src/client/p2p_client.ts +6 -1
  59. package/src/config.ts +26 -2
  60. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +312 -27
  61. package/src/msg_validators/tx_validator/archive_cache.ts +28 -0
  62. package/src/msg_validators/tx_validator/block_header_validator.ts +2 -2
  63. package/src/msg_validators/tx_validator/data_validator.ts +19 -8
  64. package/src/msg_validators/tx_validator/double_spend_validator.ts +10 -3
  65. package/src/msg_validators/tx_validator/gas_validator.ts +36 -6
  66. package/src/msg_validators/tx_validator/index.ts +1 -0
  67. package/src/msg_validators/tx_validator/metadata_validator.ts +12 -4
  68. package/src/msg_validators/tx_validator/phases_validator.ts +6 -1
  69. package/src/msg_validators/tx_validator/tx_proof_validator.ts +2 -2
  70. package/src/services/discv5/discV5_service.ts +10 -8
  71. package/src/services/dummy_service.ts +2 -1
  72. package/src/services/libp2p/libp2p_service.ts +9 -9
  73. package/src/services/peer-manager/peer_manager.ts +1 -1
  74. package/src/services/service.ts +1 -1
  75. package/src/testbench/p2p_client_testbench_worker.ts +97 -16
  76. package/src/testbench/parse_log_file.ts +4 -4
  77. package/src/testbench/testbench.ts +1 -1
  78. package/src/testbench/worker_client_manager.ts +4 -2
  79. package/src/util.ts +57 -8
@@ -1,8 +1,14 @@
1
+ import { Fr } from '@aztec/foundation/fields';
1
2
  import { toArray } from '@aztec/foundation/iterable';
2
3
  import { createLogger } from '@aztec/foundation/log';
4
+ import { ProtocolContractAddress } from '@aztec/protocol-contracts';
5
+ import { GasFees } from '@aztec/stdlib/gas';
3
6
  import { ClientIvcProof } from '@aztec/stdlib/proofs';
7
+ import { DatabasePublicStateSource } from '@aztec/stdlib/trees';
4
8
  import { Tx, TxHash } from '@aztec/stdlib/tx';
5
9
  import { getTelemetryClient } from '@aztec/telemetry-client';
10
+ import { ArchiveCache } from '../../msg_validators/tx_validator/archive_cache.js';
11
+ import { GasTxValidator } from '../../msg_validators/tx_validator/gas_validator.js';
6
12
  import { PoolInstrumentation, PoolName } from '../instrumentation.js';
7
13
  import { getPendingTxPriority } from './priority.js';
8
14
  /**
@@ -10,12 +16,18 @@ import { getPendingTxPriority } from './priority.js';
10
16
  */ export class AztecKVTxPool {
11
17
  #store;
12
18
  /** Our tx pool, stored as a Map, with K: tx hash and V: the transaction. */ #txs;
19
+ /** The maximum cumulative tx size that the pending txs in the pool take up. */ #maxTxPoolSize;
13
20
  /** Index from tx hash to the block number in which they were mined, filtered by mined txs. */ #minedTxHashToBlock;
14
21
  /** Index from tx priority (stored as hex) to its tx hash, filtered by pending txs. */ #pendingTxPriorityToHash;
22
+ /** Index from tx hash to its tx size (in bytes), filtered by pending txs. */ #pendingTxHashToSize;
23
+ /** Index from tx hash to its header hash, filtered by pending txs. */ #pendingTxHashToHeaderHash;
24
+ /** The cumulative tx size in bytes that the pending txs in the pool take up. */ #pendingTxSize;
25
+ /** In-memory mapping of pending tx hashes to the hydrated pending tx in the pool. */ #pendingTxs;
15
26
  /** KV store for archived txs. */ #archive;
16
27
  /** Archived txs map for future lookup. */ #archivedTxs;
17
28
  /** Indexes of the archived txs by insertion order. */ #archivedTxIndices;
18
29
  /** Number of txs to archive. */ #archivedTxLimit;
30
+ /** The world state synchronizer used in the node. */ #worldStateSynchronizer;
19
31
  #log;
20
32
  #metrics;
21
33
  /**
@@ -25,15 +37,21 @@ import { getPendingTxPriority } from './priority.js';
25
37
  * @param telemetry - A telemetry client.
26
38
  * @param archivedTxLimit - The number of txs to archive.
27
39
  * @param log - A logger.
28
- */ constructor(store, archive, telemetry = getTelemetryClient(), archivedTxLimit = 0, log = createLogger('p2p:tx_pool')){
40
+ */ constructor(store, archive, worldStateSynchronizer, telemetry = getTelemetryClient(), config = {}, log = createLogger('p2p:tx_pool')){
29
41
  this.#txs = store.openMap('txs');
30
42
  this.#minedTxHashToBlock = store.openMap('txHashToBlockMined');
31
43
  this.#pendingTxPriorityToHash = store.openMultiMap('pendingTxFeeToHash');
44
+ this.#pendingTxHashToSize = store.openMap('pendingTxHashToSize');
45
+ this.#pendingTxHashToHeaderHash = store.openMap('pendingTxHashToHeaderHash');
46
+ this.#pendingTxSize = store.openSingleton('pendingTxSize');
47
+ this.#maxTxPoolSize = config.maxTxPoolSize;
48
+ this.#pendingTxs = new Map();
32
49
  this.#archivedTxs = archive.openMap('archivedTxs');
33
50
  this.#archivedTxIndices = archive.openMap('archivedTxIndices');
34
- this.#archivedTxLimit = archivedTxLimit;
51
+ this.#archivedTxLimit = config.archivedTxLimit ?? 0;
35
52
  this.#store = store;
36
53
  this.#archive = archive;
54
+ this.#worldStateSynchronizer = worldStateSynchronizer;
37
55
  this.#log = log;
38
56
  this.#metrics = new PoolInstrumentation(telemetry, PoolName.TX_POOL, ()=>store.estimateSize());
39
57
  }
@@ -42,19 +60,27 @@ import { getPendingTxPriority } from './priority.js';
42
60
  return Promise.resolve();
43
61
  }
44
62
  let deletedPending = 0;
63
+ const minedNullifiers = new Set();
64
+ const minedFeePayers = new Set();
45
65
  return this.#store.transactionAsync(async ()=>{
66
+ let pendingTxSize = await this.#pendingTxSize.getAsync() ?? 0;
46
67
  for (const hash of txHashes){
47
68
  const key = hash.toString();
48
69
  await this.#minedTxHashToBlock.set(key, blockNumber);
49
- const tx = await this.getTxByHash(hash);
70
+ const tx = await this.getPendingTxByHash(hash);
50
71
  if (tx) {
72
+ const nullifiers = tx.data.getNonEmptyNullifiers();
73
+ nullifiers.forEach((nullifier)=>minedNullifiers.add(nullifier.toString()));
74
+ minedFeePayers.add(tx.data.feePayer.toString());
51
75
  deletedPending++;
52
- const fee = getPendingTxPriority(tx);
53
- await this.#pendingTxPriorityToHash.deleteValue(fee, key);
76
+ pendingTxSize -= tx.getSize();
77
+ await this.removePendingTxIndices(tx, key);
54
78
  }
55
79
  }
56
80
  this.#metrics.recordAddedObjects(txHashes.length, 'mined');
57
- this.#metrics.recordRemovedObjects(deletedPending, 'pending');
81
+ await this.#pendingTxSize.set(pendingTxSize);
82
+ const numTxsEvicted = await this.evictInvalidTxsAfterMining(txHashes, blockNumber, minedNullifiers, minedFeePayers);
83
+ this.#metrics.recordRemovedObjects(deletedPending + numTxsEvicted, 'pending');
58
84
  });
59
85
  }
60
86
  markMinedAsPending(txHashes) {
@@ -63,16 +89,24 @@ import { getPendingTxPriority } from './priority.js';
63
89
  }
64
90
  let markedAsPending = 0;
65
91
  return this.#store.transactionAsync(async ()=>{
92
+ let pendingTxSize = await this.#pendingTxSize.getAsync() ?? 0;
66
93
  for (const hash of txHashes){
67
94
  const key = hash.toString();
68
95
  await this.#minedTxHashToBlock.delete(key);
69
- const tx = await this.getTxByHash(hash);
96
+ // Rehydrate the tx in the in-memory pending txs mapping
97
+ const tx = await this.getPendingTxByHash(hash);
70
98
  if (tx) {
71
- await this.#pendingTxPriorityToHash.set(getPendingTxPriority(tx), key);
99
+ await this.addPendingTxIndices(tx, key);
100
+ pendingTxSize += tx.getSize();
72
101
  markedAsPending++;
73
102
  }
74
103
  }
75
- this.#metrics.recordAddedObjects(markedAsPending, 'pending');
104
+ await this.#pendingTxSize.set(pendingTxSize);
105
+ }).then(async ()=>{
106
+ const numInvalidTxsEvicted = await this.evictInvalidTxsAfterReorg(txHashes);
107
+ const { numLowPriorityTxsEvicted, numNewTxsEvicted } = await this.evictLowPriorityTxs(txHashes);
108
+ this.#metrics.recordAddedObjects(markedAsPending - numNewTxsEvicted, 'pending');
109
+ this.#metrics.recordRemovedObjects(numInvalidTxsEvicted + numLowPriorityTxsEvicted - numNewTxsEvicted, 'pending');
76
110
  this.#metrics.recordRemovedObjects(markedAsPending, 'mined');
77
111
  });
78
112
  }
@@ -140,6 +174,7 @@ import { getPendingTxPriority } from './priority.js';
140
174
  })));
141
175
  await this.#store.transactionAsync(async ()=>{
142
176
  let pendingCount = 0;
177
+ let pendingTxSize = await this.#pendingTxSize.getAsync() ?? 0;
143
178
  await Promise.all(txs.map(async (tx, i)=>{
144
179
  const { txHash, txStats } = hashesAndStats[i];
145
180
  this.#log.verbose(`Adding tx ${txHash.toString()} to pool`, {
@@ -150,42 +185,47 @@ import { getPendingTxPriority } from './priority.js';
150
185
  await this.#txs.set(key, tx.toBuffer());
151
186
  if (!await this.#minedTxHashToBlock.hasAsync(key)) {
152
187
  pendingCount++;
153
- // REFACTOR: Use an lmdb conditional write to avoid race conditions with this write tx
154
- await this.#pendingTxPriorityToHash.set(getPendingTxPriority(tx), key);
188
+ pendingTxSize += tx.getSize();
189
+ await this.addPendingTxIndices(tx, key);
155
190
  this.#metrics.recordSize(tx);
156
191
  }
157
192
  }));
158
- this.#metrics.recordAddedObjects(pendingCount, 'pending');
193
+ await this.#pendingTxSize.set(pendingTxSize);
194
+ const { numLowPriorityTxsEvicted, numNewTxsEvicted } = await this.evictLowPriorityTxs(hashesAndStats.map(({ txHash })=>txHash));
195
+ this.#metrics.recordAddedObjects(pendingCount - numNewTxsEvicted, 'pending');
196
+ this.#metrics.recordRemovedObjects(numLowPriorityTxsEvicted - numNewTxsEvicted, 'pending');
159
197
  });
160
198
  }
161
199
  /**
162
200
  * Deletes transactions from the pool. Tx hashes that are not present are ignored.
163
201
  * @param txHashes - An array of tx hashes to be removed from the tx pool.
164
202
  * @returns Empty promise.
165
- */ deleteTxs(txHashes) {
203
+ */ deleteTxs(txHashes, eviction = false) {
166
204
  let pendingDeleted = 0;
167
205
  let minedDeleted = 0;
168
206
  const deletedTxs = [];
169
207
  const poolDbTx = this.#store.transactionAsync(async ()=>{
208
+ let pendingTxSize = await this.#pendingTxSize.getAsync() ?? 0;
170
209
  for (const hash of txHashes){
171
210
  const key = hash.toString();
172
211
  const tx = await this.getTxByHash(hash);
173
212
  if (tx) {
174
- const fee = getPendingTxPriority(tx);
175
- await this.#pendingTxPriorityToHash.deleteValue(fee, key);
176
213
  const isMined = await this.#minedTxHashToBlock.hasAsync(key);
177
214
  if (isMined) {
178
215
  minedDeleted++;
179
216
  } else {
180
217
  pendingDeleted++;
218
+ pendingTxSize -= tx.getSize();
219
+ await this.removePendingTxIndices(tx, key);
181
220
  }
182
- if (this.#archivedTxLimit) {
221
+ if (!eviction && this.#archivedTxLimit) {
183
222
  deletedTxs.push(tx);
184
223
  }
185
224
  await this.#txs.delete(key);
186
225
  await this.#minedTxHashToBlock.delete(key);
187
226
  }
188
227
  }
228
+ await this.#pendingTxSize.set(pendingTxSize);
189
229
  this.#metrics.recordRemovedObjects(pendingDeleted, 'pending');
190
230
  this.#metrics.recordRemovedObjects(minedDeleted, 'mined');
191
231
  });
@@ -210,6 +250,43 @@ import { getPendingTxPriority } from './priority.js';
210
250
  return vals.map((x)=>TxHash.fromString(x));
211
251
  }
212
252
  /**
253
+ * Creates a GasTxValidator instance.
254
+ * @param db - DB for the validator to use
255
+ * @returns A GasTxValidator instance
256
+ */ createGasTxValidator(db) {
257
+ return new GasTxValidator(new DatabasePublicStateSource(db), ProtocolContractAddress.FeeJuice, GasFees.empty());
258
+ }
259
+ /**
260
+ * Creates an ArchiveCache instance.
261
+ * @param db - DB for the cache to use
262
+ * @returns An ArchiveCache instance
263
+ */ createArchiveCache(db) {
264
+ return new ArchiveCache(db);
265
+ }
266
+ /**
267
+ * Checks if a cached transaction exists in the in-memory pending tx pool and returns it.
268
+ * Otherwise, it checks the tx pool, updates the pending tx pool, and returns the tx.
269
+ * @param txHash - The generated tx hash.
270
+ * @returns The transaction, if found, 'undefined' otherwise.
271
+ */ async getPendingTxByHash(txHash) {
272
+ let key;
273
+ if (typeof txHash === 'string') {
274
+ key = txHash;
275
+ txHash = TxHash.fromString(txHash);
276
+ } else {
277
+ key = txHash.toString();
278
+ }
279
+ if (this.#pendingTxs.has(key)) {
280
+ return this.#pendingTxs.get(key);
281
+ }
282
+ const tx = await this.getTxByHash(txHash);
283
+ if (tx) {
284
+ this.#pendingTxs.set(key, tx);
285
+ return tx;
286
+ }
287
+ return undefined;
288
+ }
289
+ /**
213
290
  * Archives a list of txs for future reference. The number of archived txs is limited by the specified archivedTxLimit.
214
291
  * @param txs - The list of transactions to archive.
215
292
  * @returns Empty promise.
@@ -242,4 +319,140 @@ import { getPendingTxPriority } from './priority.js';
242
319
  }
243
320
  });
244
321
  }
322
+ /**
323
+ * Evicts pending txs with the lowest priority fees from the pool to accomodate the max tx count and cumulative max tx size
324
+ * after new txs are added.
325
+ *
326
+ * @param newTxHashes - The tx hashes of the new txs added to the pool.
327
+ * @returns The total number of txs evicted from the pool and the number of new txs that were evicted.
328
+ */ async evictLowPriorityTxs(newTxHashes) {
329
+ if (this.#maxTxPoolSize === undefined) {
330
+ return {
331
+ numLowPriorityTxsEvicted: 0,
332
+ numNewTxsEvicted: 0
333
+ };
334
+ }
335
+ let numNewTxsEvicted = 0;
336
+ const txsToEvict = [];
337
+ let pendingTxsSize = await this.#pendingTxSize.getAsync() ?? 0;
338
+ if (pendingTxsSize > this.#maxTxPoolSize) {
339
+ for await (const txHash of this.#pendingTxPriorityToHash.valuesAsync()){
340
+ this.#log.verbose(`Evicting tx ${txHash} from pool due to low priority to satisfy max tx size limit`);
341
+ txsToEvict.push(TxHash.fromString(txHash));
342
+ const txSize = await this.#pendingTxHashToSize.getAsync(txHash.toString()) ?? (await this.getPendingTxByHash(txHash))?.getSize();
343
+ if (txSize) {
344
+ pendingTxsSize -= txSize;
345
+ if (pendingTxsSize <= this.#maxTxPoolSize) {
346
+ break;
347
+ }
348
+ }
349
+ }
350
+ numNewTxsEvicted += newTxHashes.filter((txHash)=>txsToEvict.includes(txHash)).length;
351
+ }
352
+ if (txsToEvict.length > 0) {
353
+ await this.deleteTxs(txsToEvict, true);
354
+ }
355
+ return {
356
+ numLowPriorityTxsEvicted: txsToEvict.length,
357
+ numNewTxsEvicted
358
+ };
359
+ }
360
+ /**
361
+ * Evicts invalid pending txs from the pool after a txs from a block are mined.
362
+ * Eviction criteria includes:
363
+ * - txs with nullifiers that are already included in the mined block
364
+ * - txs with an insufficient fee payer balance
365
+ * - txs with a max block number lower than the mined block
366
+ *
367
+ * @param minedTxHashes - The tx hashes of the txs mined in the block.
368
+ * @param blockNumber - The block number of the mined block.
369
+ * @returns The total number of txs evicted from the pool.
370
+ */ async evictInvalidTxsAfterMining(minedTxHashes, blockNumber, minedNullifiers, minedFeePayers) {
371
+ if (minedTxHashes.length === 0) {
372
+ return 0;
373
+ }
374
+ // Wait for world state to be synced to at least the mined block number
375
+ await this.#worldStateSynchronizer.syncImmediate(blockNumber);
376
+ const db = this.#worldStateSynchronizer.getCommitted();
377
+ const gasTxValidator = this.createGasTxValidator(db);
378
+ const txsToEvict = [];
379
+ for await (const txHash of this.#pendingTxPriorityToHash.valuesAsync()){
380
+ const tx = await this.getPendingTxByHash(txHash);
381
+ if (!tx) {
382
+ continue;
383
+ }
384
+ // Evict pending txs that share nullifiers with mined txs
385
+ const txNullifiers = tx.data.getNonEmptyNullifiers();
386
+ if (txNullifiers.some((nullifier)=>minedNullifiers.has(nullifier.toString()))) {
387
+ this.#log.verbose(`Evicting tx ${txHash} from pool due to a duplicate nullifier with a mined tx`);
388
+ txsToEvict.push(TxHash.fromString(txHash));
389
+ continue;
390
+ }
391
+ // Evict pending txs with an insufficient fee payer balance
392
+ if (minedFeePayers.has(tx.data.feePayer.toString()) && (await gasTxValidator.validateTxFee(tx)).result === 'invalid') {
393
+ this.#log.verbose(`Evicting tx ${txHash} from pool due to an insufficient fee payer balance`);
394
+ txsToEvict.push(TxHash.fromString(txHash));
395
+ continue;
396
+ }
397
+ // Evict pending txs with a max block number less than or equal to the mined block
398
+ const maxBlockNumber = tx.data.rollupValidationRequests.maxBlockNumber;
399
+ if (maxBlockNumber.isSome && maxBlockNumber.value.toNumber() <= blockNumber) {
400
+ this.#log.verbose(`Evicting tx ${txHash} from pool due to an invalid max block number`);
401
+ txsToEvict.push(TxHash.fromString(txHash));
402
+ continue;
403
+ }
404
+ }
405
+ if (txsToEvict.length > 0) {
406
+ await this.deleteTxs(txsToEvict, true);
407
+ }
408
+ return txsToEvict.length;
409
+ }
410
+ /**
411
+ * Evicts pending txs that no longer have valid archive roots or fee payer balances from the pool after a reorg.
412
+ *
413
+ * @param txHashes - The tx hashes of the txs that were moved from mined to pending.
414
+ * @returns The total number of txs evicted from the pool.
415
+ */ async evictInvalidTxsAfterReorg(txHashes) {
416
+ if (txHashes.length === 0) {
417
+ return 0;
418
+ }
419
+ await this.#worldStateSynchronizer.syncImmediate();
420
+ const db = this.#worldStateSynchronizer.getCommitted();
421
+ const archiveCache = this.createArchiveCache(db);
422
+ const gasTxValidator = this.createGasTxValidator(db);
423
+ const txsToEvict = [];
424
+ for await (const [txHash, headerHash] of this.#pendingTxHashToHeaderHash.entriesAsync()){
425
+ const tx = await this.getPendingTxByHash(txHash);
426
+ if (!tx) {
427
+ continue;
428
+ }
429
+ const [index] = await archiveCache.getArchiveIndices([
430
+ Fr.fromString(headerHash)
431
+ ]);
432
+ if (index === undefined) {
433
+ this.#log.verbose(`Evicting tx ${txHash} from pool due to an invalid archive root`);
434
+ txsToEvict.push(TxHash.fromString(txHash));
435
+ continue;
436
+ }
437
+ if ((await gasTxValidator.validateTxFee(tx)).result === 'invalid') {
438
+ this.#log.verbose(`Evicting tx ${txHash} from pool due to an insufficient fee payer balance`);
439
+ txsToEvict.push(TxHash.fromString(txHash));
440
+ }
441
+ }
442
+ if (txsToEvict.length > 0) {
443
+ await this.deleteTxs(txsToEvict, true);
444
+ }
445
+ return txsToEvict.length;
446
+ }
447
+ async addPendingTxIndices(tx, txHash) {
448
+ await this.#pendingTxPriorityToHash.set(getPendingTxPriority(tx), txHash);
449
+ await this.#pendingTxHashToSize.set(txHash, tx.getSize());
450
+ await this.#pendingTxHashToHeaderHash.set(txHash, (await tx.data.constants.historicalHeader.hash()).toString());
451
+ }
452
+ async removePendingTxIndices(tx, txHash) {
453
+ await this.#pendingTxPriorityToHash.deleteValue(getPendingTxPriority(tx), txHash);
454
+ await this.#pendingTxHashToSize.delete(txHash);
455
+ await this.#pendingTxHashToHeaderHash.delete(txHash);
456
+ this.#pendingTxs.delete(txHash);
457
+ }
245
458
  }
@@ -0,0 +1,14 @@
1
+ import type { Fr } from '@aztec/foundation/fields';
2
+ import type { ArchiveSource } from '@aztec/p2p';
3
+ import type { MerkleTreeReadOperations } from '@aztec/stdlib/interfaces/server';
4
+ /**
5
+ * Implements an archive source by checking a DB and an in-memory collection.
6
+ * Intended for validating transactions as they are added to a block.
7
+ */
8
+ export declare class ArchiveCache implements ArchiveSource {
9
+ private db;
10
+ archives: Map<string, bigint>;
11
+ constructor(db: MerkleTreeReadOperations);
12
+ getArchiveIndices(archives: Fr[]): Promise<(bigint | undefined)[]>;
13
+ }
14
+ //# sourceMappingURL=archive_cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archive_cache.d.ts","sourceRoot":"","sources":["../../../src/msg_validators/tx_validator/archive_cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,iCAAiC,CAAC;AAGhF;;;GAGG;AACH,qBAAa,YAAa,YAAW,aAAa;IAGpC,OAAO,CAAC,EAAE;IAFtB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAEV,EAAE,EAAE,wBAAwB;IAInC,iBAAiB,CAAC,QAAQ,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC;CAWhF"}
@@ -0,0 +1,22 @@
1
+ import { MerkleTreeId } from '@aztec/stdlib/trees';
2
+ /**
3
+ * Implements an archive source by checking a DB and an in-memory collection.
4
+ * Intended for validating transactions as they are added to a block.
5
+ */ export class ArchiveCache {
6
+ db;
7
+ archives;
8
+ constructor(db){
9
+ this.db = db;
10
+ this.archives = new Map();
11
+ }
12
+ async getArchiveIndices(archives) {
13
+ const toCheckDb = archives.filter((n)=>!this.archives.has(n.toString()));
14
+ const dbHits = await this.db.findLeafIndices(MerkleTreeId.ARCHIVE, toCheckDb);
15
+ dbHits.forEach((x, index)=>{
16
+ if (x !== undefined) {
17
+ this.archives.set(toCheckDb[index].toString(), x);
18
+ }
19
+ });
20
+ return archives.map((n)=>this.archives.get(n.toString()));
21
+ }
22
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"block_header_validator.d.ts","sourceRoot":"","sources":["../../../src/msg_validators/tx_validator/block_header_validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAEnD,OAAO,EAAE,KAAK,KAAK,EAAM,KAAK,kBAAkB,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE7F,MAAM,WAAW,aAAa;IAC5B,iBAAiB,EAAE,CAAC,QAAQ,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;CACxE;AAED,qBAAa,sBAAsB,CAAC,CAAC,SAAS,KAAK,CAAE,YAAW,WAAW,CAAC,CAAC,CAAC;;gBAIhE,aAAa,EAAE,aAAa;IAIlC,UAAU,CAAC,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAQrD"}
1
+ {"version":3,"file":"block_header_validator.d.ts","sourceRoot":"","sources":["../../../src/msg_validators/tx_validator/block_header_validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAEnD,OAAO,EAAE,KAAK,KAAK,EAA6B,KAAK,kBAAkB,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpH,MAAM,WAAW,aAAa;IAC5B,iBAAiB,EAAE,CAAC,QAAQ,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;CACxE;AAED,qBAAa,sBAAsB,CAAC,CAAC,SAAS,KAAK,CAAE,YAAW,WAAW,CAAC,CAAC,CAAC;;gBAIhE,aAAa,EAAE,aAAa;IAIlC,UAAU,CAAC,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAQrD"}
@@ -1,5 +1,5 @@
1
1
  import { createLogger } from '@aztec/foundation/log';
2
- import { Tx } from '@aztec/stdlib/tx';
2
+ import { TX_ERROR_BLOCK_HEADER, Tx } from '@aztec/stdlib/tx';
3
3
  export class BlockHeaderTxValidator {
4
4
  #log = createLogger('p2p:tx_validator:tx_block_header');
5
5
  #archiveSource;
@@ -15,7 +15,7 @@ export class BlockHeaderTxValidator {
15
15
  return {
16
16
  result: 'invalid',
17
17
  reason: [
18
- 'Block header not found'
18
+ TX_ERROR_BLOCK_HEADER
19
19
  ]
20
20
  };
21
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"data_validator.d.ts","sourceRoot":"","sources":["../../../src/msg_validators/tx_validator/data_validator.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,EAAE,EAAE,KAAK,kBAAkB,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEjF,qBAAa,eAAgB,YAAW,WAAW,CAAC,EAAE,CAAC;;IAG/C,UAAU,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAmFtD"}
1
+ {"version":3,"file":"data_validator.d.ts","sourceRoot":"","sources":["../../../src/msg_validators/tx_validator/data_validator.ts"],"names":[],"mappings":"AAGA,OAAO,EAQL,EAAE,EACF,KAAK,kBAAkB,EACvB,KAAK,WAAW,EACjB,MAAM,kBAAkB,CAAC;AAE1B,qBAAa,eAAgB,YAAW,WAAW,CAAC,EAAE,CAAC;;IAG/C,UAAU,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAmFtD"}
@@ -1,7 +1,7 @@
1
1
  import { MAX_FR_CALLDATA_TO_ALL_ENQUEUED_CALLS } from '@aztec/constants';
2
2
  import { createLogger } from '@aztec/foundation/log';
3
3
  import { computeCalldataHash } from '@aztec/stdlib/hash';
4
- import { Tx } from '@aztec/stdlib/tx';
4
+ import { TX_ERROR_CALLDATA_COUNT_MISMATCH, TX_ERROR_CALLDATA_COUNT_TOO_LARGE, TX_ERROR_CONTRACT_CLASS_LOGS, TX_ERROR_CONTRACT_CLASS_LOG_COUNT, TX_ERROR_CONTRACT_CLASS_LOG_LENGTH, TX_ERROR_CONTRACT_CLASS_LOG_SORTING, TX_ERROR_INCORRECT_CALLDATA, Tx } from '@aztec/stdlib/tx';
5
5
  export class DataTxValidator {
6
6
  #log = createLogger('p2p:tx_validator:tx_data');
7
7
  async validateTx(tx) {
@@ -11,7 +11,7 @@ export class DataTxValidator {
11
11
  }
12
12
  async #hasCorrectCalldata(tx) {
13
13
  if (tx.publicFunctionCalldata.length !== tx.numberOfPublicCalls()) {
14
- const reason = 'Wrong number of calldata for public calls';
14
+ const reason = TX_ERROR_CALLDATA_COUNT_MISMATCH;
15
15
  this.#log.warn(`Rejecting tx ${await Tx.getHash(tx)}. Reason: ${reason}. Expected ${tx.numberOfPublicCalls()}. Got ${tx.publicFunctionCalldata.length}.`);
16
16
  return {
17
17
  result: 'invalid',
@@ -21,7 +21,7 @@ export class DataTxValidator {
21
21
  };
22
22
  }
23
23
  if (tx.getTotalPublicCalldataCount() > MAX_FR_CALLDATA_TO_ALL_ENQUEUED_CALLS) {
24
- const reason = 'Total calldata too large for enqueued public calls';
24
+ const reason = TX_ERROR_CALLDATA_COUNT_TOO_LARGE;
25
25
  this.#log.warn(`Rejecting tx ${await Tx.getHash(tx)}. Reason: ${reason}. Expected no greater than ${MAX_FR_CALLDATA_TO_ALL_ENQUEUED_CALLS} fields. Got ${tx.getTotalPublicCalldataCount()}.`);
26
26
  return {
27
27
  result: 'invalid',
@@ -35,7 +35,7 @@ export class DataTxValidator {
35
35
  const { request, calldata } = callRequests[i];
36
36
  const hash = await computeCalldataHash(calldata);
37
37
  if (!hash.equals(request.calldataHash)) {
38
- const reason = 'Incorrect calldata for public call';
38
+ const reason = TX_ERROR_INCORRECT_CALLDATA;
39
39
  this.#log.warn(`Rejecting tx ${await Tx.getHash(tx)}. Reason: ${reason}. Call request index: ${i}.`);
40
40
  return {
41
41
  result: 'invalid',
@@ -57,7 +57,7 @@ export class DataTxValidator {
57
57
  return {
58
58
  result: 'invalid',
59
59
  reason: [
60
- 'Mismatched number of contract class logs'
60
+ TX_ERROR_CONTRACT_CLASS_LOG_COUNT
61
61
  ]
62
62
  };
63
63
  }
@@ -70,7 +70,7 @@ export class DataTxValidator {
70
70
  return {
71
71
  result: 'invalid',
72
72
  reason: [
73
- 'Incorrectly sorted contract class logs'
73
+ TX_ERROR_CONTRACT_CLASS_LOG_SORTING
74
74
  ]
75
75
  };
76
76
  } else {
@@ -78,7 +78,7 @@ export class DataTxValidator {
78
78
  return {
79
79
  result: 'invalid',
80
80
  reason: [
81
- 'Mismatched contract class logs'
81
+ TX_ERROR_CONTRACT_CLASS_LOGS
82
82
  ]
83
83
  };
84
84
  }
@@ -88,7 +88,7 @@ export class DataTxValidator {
88
88
  return {
89
89
  result: 'invalid',
90
90
  reason: [
91
- 'Mismatched contract class logs length'
91
+ TX_ERROR_CONTRACT_CLASS_LOG_LENGTH
92
92
  ]
93
93
  };
94
94
  }
@@ -1 +1 @@
1
- {"version":3,"file":"double_spend_validator.d.ts","sourceRoot":"","sources":["../../../src/msg_validators/tx_validator/double_spend_validator.ts"],"names":[],"mappings":";;AACA,OAAO,EAAE,KAAK,KAAK,EAAM,KAAK,kBAAkB,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE7F,MAAM,WAAW,eAAe;IAC9B,eAAe,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;CAC/D;AAED,qBAAa,sBAAsB,CAAC,CAAC,SAAS,KAAK,CAAE,YAAW,WAAW,CAAC,CAAC,CAAC;;gBAIhE,eAAe,EAAE,eAAe;IAItC,UAAU,CAAC,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAiBrD"}
1
+ {"version":3,"file":"double_spend_validator.d.ts","sourceRoot":"","sources":["../../../src/msg_validators/tx_validator/double_spend_validator.ts"],"names":[],"mappings":";;AACA,OAAO,EACL,KAAK,KAAK,EAIV,KAAK,kBAAkB,EACvB,KAAK,WAAW,EACjB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,eAAe;IAC9B,eAAe,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;CAC/D;AAED,qBAAa,sBAAsB,CAAC,CAAC,SAAS,KAAK,CAAE,YAAW,WAAW,CAAC,CAAC,CAAC;;gBAIhE,eAAe,EAAE,eAAe;IAItC,UAAU,CAAC,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAiBrD"}
@@ -1,5 +1,5 @@
1
1
  import { createLogger } from '@aztec/foundation/log';
2
- import { Tx } from '@aztec/stdlib/tx';
2
+ import { TX_ERROR_DUPLICATE_NULLIFIER_IN_TX, TX_ERROR_EXISTING_NULLIFIER, Tx } from '@aztec/stdlib/tx';
3
3
  export class DoubleSpendTxValidator {
4
4
  #log = createLogger('p2p:tx_validator:tx_double_spend');
5
5
  #nullifierSource;
@@ -15,7 +15,7 @@ export class DoubleSpendTxValidator {
15
15
  return {
16
16
  result: 'invalid',
17
17
  reason: [
18
- 'Duplicate nullifier in tx'
18
+ TX_ERROR_DUPLICATE_NULLIFIER_IN_TX
19
19
  ]
20
20
  };
21
21
  }
@@ -24,7 +24,7 @@ export class DoubleSpendTxValidator {
24
24
  return {
25
25
  result: 'invalid',
26
26
  reason: [
27
- 'Existing nullifier'
27
+ TX_ERROR_EXISTING_NULLIFIER
28
28
  ]
29
29
  };
30
30
  }
@@ -1,10 +1,11 @@
1
1
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
2
- import type { GasFees } from '@aztec/stdlib/gas';
2
+ import { GasFees } from '@aztec/stdlib/gas';
3
3
  import type { PublicStateSource } from '@aztec/stdlib/trees';
4
4
  import { type Tx, type TxValidationResult, type TxValidator } from '@aztec/stdlib/tx';
5
5
  export declare class GasTxValidator implements TxValidator<Tx> {
6
6
  #private;
7
7
  constructor(publicDataSource: PublicStateSource, feeJuiceAddress: AztecAddress, gasFees: GasFees);
8
8
  validateTx(tx: Tx): Promise<TxValidationResult>;
9
+ validateTxFee(tx: Tx): Promise<TxValidationResult>;
9
10
  }
10
11
  //# sourceMappingURL=gas_validator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gas_validator.d.ts","sourceRoot":"","sources":["../../../src/msg_validators/tx_validator/gas_validator.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,KAAK,EAAE,EAAoB,KAAK,kBAAkB,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAExG,qBAAa,cAAe,YAAW,WAAW,CAAC,EAAE,CAAC;;gBAMxC,gBAAgB,EAAE,iBAAiB,EAAE,eAAe,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO;IAM1F,UAAU,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAyEtD"}
1
+ {"version":3,"file":"gas_validator.d.ts","sourceRoot":"","sources":["../../../src/msg_validators/tx_validator/gas_validator.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAO,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAIL,KAAK,EAAE,EAEP,KAAK,kBAAkB,EACvB,KAAK,WAAW,EACjB,MAAM,kBAAkB,CAAC;AAE1B,qBAAa,cAAe,YAAW,WAAW,CAAC,EAAE,CAAC;;gBAMxC,gBAAgB,EAAE,iBAAiB,EAAE,eAAe,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO;IAM1F,UAAU,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAoDxC,aAAa,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC;CA0ChE"}
@@ -1,8 +1,10 @@
1
+ import { FIXED_DA_GAS, FIXED_L2_GAS } from '@aztec/constants';
1
2
  import { createLogger } from '@aztec/foundation/log';
2
3
  import { computeFeePayerBalanceStorageSlot } from '@aztec/protocol-contracts/fee-juice';
3
4
  import { getCallRequestsWithCalldataByPhase } from '@aztec/simulator/server';
4
5
  import { FunctionSelector } from '@aztec/stdlib/abi';
5
- import { TxExecutionPhase } from '@aztec/stdlib/tx';
6
+ import { Gas } from '@aztec/stdlib/gas';
7
+ import { TX_ERROR_INSUFFICIENT_FEE_PAYER_BALANCE, TX_ERROR_INSUFFICIENT_FEE_PER_GAS, TX_ERROR_INSUFFICIENT_GAS_LIMIT, TxExecutionPhase } from '@aztec/stdlib/tx';
6
8
  export class GasTxValidator {
7
9
  #log = createLogger('sequencer:tx_validator:tx_gas');
8
10
  #publicDataSource;
@@ -14,15 +16,19 @@ export class GasTxValidator {
14
16
  this.#gasFees = gasFees;
15
17
  }
16
18
  async validateTx(tx) {
19
+ const gasLimitValidation = this.#validateMinGasLimit(tx);
20
+ if (gasLimitValidation.result === 'invalid') {
21
+ return Promise.resolve(gasLimitValidation);
22
+ }
17
23
  if (await this.#shouldSkip(tx)) {
18
24
  return Promise.resolve({
19
25
  result: 'skipped',
20
26
  reason: [
21
- 'Insufficient fee per gas'
27
+ TX_ERROR_INSUFFICIENT_FEE_PER_GAS
22
28
  ]
23
29
  });
24
30
  }
25
- return this.#validateTxFee(tx);
31
+ return this.validateTxFee(tx);
26
32
  }
27
33
  /**
28
34
  * Check whether the tx's max fees are valid for the current block, and skip if not.
@@ -42,7 +48,28 @@ export class GasTxValidator {
42
48
  }
43
49
  return notEnoughMaxFees;
44
50
  }
45
- async #validateTxFee(tx) {
51
+ /**
52
+ * Check whether the tx's gas limit is above the minimum amount.
53
+ */ #validateMinGasLimit(tx) {
54
+ const gasLimits = tx.data.constants.txContext.gasSettings.gasLimits;
55
+ const minGasLimits = new Gas(FIXED_DA_GAS, FIXED_L2_GAS);
56
+ if (minGasLimits.gtAny(gasLimits)) {
57
+ this.#log.warn(`Rejecting transaction due to the gas limit(s) not being above the minimum gas limit`, {
58
+ gasLimits,
59
+ minGasLimits
60
+ });
61
+ return {
62
+ result: 'invalid',
63
+ reason: [
64
+ TX_ERROR_INSUFFICIENT_GAS_LIMIT
65
+ ]
66
+ };
67
+ }
68
+ return {
69
+ result: 'valid'
70
+ };
71
+ }
72
+ async validateTxFee(tx) {
46
73
  const feePayer = tx.data.feePayer;
47
74
  // Compute the maximum fee that this tx may pay, based on its gasLimits and maxFeePerGas
48
75
  const feeLimit = tx.data.constants.txContext.gasSettings.getFeeLimit();
@@ -65,7 +92,7 @@ export class GasTxValidator {
65
92
  return {
66
93
  result: 'invalid',
67
94
  reason: [
68
- 'Insufficient fee payer balance'
95
+ TX_ERROR_INSUFFICIENT_FEE_PAYER_BALANCE
69
96
  ]
70
97
  };
71
98
  }
@@ -8,4 +8,5 @@ export * from './gas_validator.js';
8
8
  export * from './phases_validator.js';
9
9
  export * from './test_utils.js';
10
10
  export * from './allowed_public_setup.js';
11
+ export * from './archive_cache.js';
11
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/msg_validators/tx_validator/index.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/msg_validators/tx_validator/index.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,oBAAoB,CAAC;AACnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC"}
@@ -8,3 +8,4 @@ export * from './gas_validator.js';
8
8
  export * from './phases_validator.js';
9
9
  export * from './test_utils.js';
10
10
  export * from './allowed_public_setup.js';
11
+ export * from './archive_cache.js';