@aztec/p2p 0.86.0-starknet.1 → 0.87.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 (132) hide show
  1. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  2. package/dest/client/factory.d.ts +1 -1
  3. package/dest/client/factory.d.ts.map +1 -1
  4. package/dest/client/factory.js +11 -13
  5. package/dest/client/interface.d.ts +10 -1
  6. package/dest/client/interface.d.ts.map +1 -1
  7. package/dest/client/p2p_client.d.ts +18 -6
  8. package/dest/client/p2p_client.d.ts.map +1 -1
  9. package/dest/client/p2p_client.js +103 -53
  10. package/dest/config.d.ts +5 -6
  11. package/dest/config.d.ts.map +1 -1
  12. package/dest/config.js +6 -11
  13. package/dest/enr/generate-enr.d.ts +7 -0
  14. package/dest/enr/generate-enr.d.ts.map +1 -1
  15. package/dest/enr/generate-enr.js +23 -2
  16. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
  17. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  18. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +3 -0
  19. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
  20. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +44 -14
  21. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +2 -0
  22. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
  23. package/dest/mem_pools/tx_pool/memory_tx_pool.js +6 -0
  24. package/dest/mem_pools/tx_pool/tx_pool.d.ts +7 -0
  25. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +1 -1
  26. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +1 -1
  27. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +13 -5
  28. package/dest/msg_validators/tx_validator/block_header_validator.js +1 -1
  29. package/dest/msg_validators/tx_validator/data_validator.d.ts.map +1 -1
  30. package/dest/msg_validators/tx_validator/data_validator.js +15 -14
  31. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +0 -2
  32. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
  33. package/dest/msg_validators/tx_validator/double_spend_validator.js +2 -2
  34. package/dest/msg_validators/tx_validator/factory.d.ts +14 -0
  35. package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -0
  36. package/dest/msg_validators/tx_validator/factory.js +62 -0
  37. package/dest/msg_validators/tx_validator/gas_validator.js +3 -3
  38. package/dest/msg_validators/tx_validator/metadata_validator.js +5 -5
  39. package/dest/msg_validators/tx_validator/phases_validator.d.ts.map +1 -1
  40. package/dest/msg_validators/tx_validator/phases_validator.js +1 -1
  41. package/dest/msg_validators/tx_validator/tx_proof_validator.js +1 -1
  42. package/dest/services/discv5/discV5_service.d.ts +2 -2
  43. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  44. package/dest/services/discv5/discV5_service.js +9 -13
  45. package/dest/services/dummy_service.d.ts +3 -3
  46. package/dest/services/dummy_service.d.ts.map +1 -1
  47. package/dest/services/dummy_service.js +6 -1
  48. package/dest/services/encoding.d.ts +1 -3
  49. package/dest/services/encoding.d.ts.map +1 -1
  50. package/dest/services/libp2p/libp2p_service.d.ts +4 -2
  51. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  52. package/dest/services/libp2p/libp2p_service.js +87 -88
  53. package/dest/services/peer-manager/metrics.d.ts.map +1 -1
  54. package/dest/services/peer-manager/peer_manager.d.ts +1 -1
  55. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  56. package/dest/services/peer-manager/peer_manager.js +11 -2
  57. package/dest/services/reqresp/connection-sampler/batch_connection_sampler.d.ts.map +1 -1
  58. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts +2 -2
  59. package/dest/services/reqresp/connection-sampler/connection_sampler.d.ts.map +1 -1
  60. package/dest/services/reqresp/connection-sampler/connection_sampler.js +41 -21
  61. package/dest/services/reqresp/interface.d.ts +1 -3
  62. package/dest/services/reqresp/interface.d.ts.map +1 -1
  63. package/dest/services/reqresp/metrics.d.ts.map +1 -1
  64. package/dest/services/reqresp/protocols/goodbye.d.ts +0 -2
  65. package/dest/services/reqresp/protocols/goodbye.d.ts.map +1 -1
  66. package/dest/services/reqresp/protocols/goodbye.js +1 -1
  67. package/dest/services/reqresp/protocols/ping.d.ts +0 -2
  68. package/dest/services/reqresp/protocols/ping.d.ts.map +1 -1
  69. package/dest/services/reqresp/protocols/status.d.ts +0 -2
  70. package/dest/services/reqresp/protocols/status.d.ts.map +1 -1
  71. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
  72. package/dest/services/reqresp/reqresp.d.ts +1 -3
  73. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  74. package/dest/services/reqresp/reqresp.js +2 -2
  75. package/dest/services/service.d.ts +3 -2
  76. package/dest/services/service.d.ts.map +1 -1
  77. package/dest/test-helpers/get-ports.d.ts.map +1 -1
  78. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  79. package/dest/test-helpers/make-test-p2p-clients.js +2 -2
  80. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  81. package/dest/test-helpers/reqresp-nodes.js +1 -1
  82. package/dest/testbench/p2p_client_testbench_worker.js +9 -6
  83. package/dest/testbench/testbench.js +1 -1
  84. package/dest/testbench/worker_client_manager.d.ts +0 -1
  85. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  86. package/dest/testbench/worker_client_manager.js +2 -2
  87. package/dest/types/index.d.ts +1 -0
  88. package/dest/types/index.d.ts.map +1 -1
  89. package/dest/types/index.js +1 -0
  90. package/dest/versioning.d.ts +2 -2
  91. package/dest/versioning.d.ts.map +1 -1
  92. package/dest/versioning.js +6 -1
  93. package/package.json +15 -15
  94. package/src/bootstrap/bootstrap.ts +1 -1
  95. package/src/client/factory.ts +33 -32
  96. package/src/client/interface.ts +13 -1
  97. package/src/client/p2p_client.ts +129 -55
  98. package/src/config.ts +11 -18
  99. package/src/enr/generate-enr.ts +33 -3
  100. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +2 -2
  101. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +4 -1
  102. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +72 -34
  103. package/src/mem_pools/tx_pool/memory_tx_pool.ts +12 -1
  104. package/src/mem_pools/tx_pool/tx_pool.ts +9 -0
  105. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +9 -3
  106. package/src/msg_validators/tx_validator/block_header_validator.ts +1 -1
  107. package/src/msg_validators/tx_validator/data_validator.ts +24 -18
  108. package/src/msg_validators/tx_validator/double_spend_validator.ts +2 -2
  109. package/src/msg_validators/tx_validator/factory.ts +94 -0
  110. package/src/msg_validators/tx_validator/gas_validator.ts +3 -3
  111. package/src/msg_validators/tx_validator/metadata_validator.ts +5 -5
  112. package/src/msg_validators/tx_validator/phases_validator.ts +6 -2
  113. package/src/msg_validators/tx_validator/tx_proof_validator.ts +1 -1
  114. package/src/services/discv5/discV5_service.ts +14 -12
  115. package/src/services/dummy_service.ts +8 -2
  116. package/src/services/libp2p/libp2p_service.ts +95 -107
  117. package/src/services/peer-manager/metrics.ts +4 -1
  118. package/src/services/peer-manager/peer_manager.ts +18 -1
  119. package/src/services/reqresp/connection-sampler/batch_connection_sampler.ts +5 -1
  120. package/src/services/reqresp/connection-sampler/connection_sampler.ts +42 -19
  121. package/src/services/reqresp/metrics.ts +4 -1
  122. package/src/services/reqresp/protocols/goodbye.ts +1 -1
  123. package/src/services/reqresp/rate-limiter/rate_limiter.ts +4 -1
  124. package/src/services/reqresp/reqresp.ts +2 -1
  125. package/src/services/service.ts +4 -1
  126. package/src/test-helpers/make-test-p2p-clients.ts +2 -1
  127. package/src/test-helpers/reqresp-nodes.ts +1 -1
  128. package/src/testbench/p2p_client_testbench_worker.ts +8 -4
  129. package/src/testbench/testbench.ts +1 -1
  130. package/src/testbench/worker_client_manager.ts +2 -2
  131. package/src/types/index.ts +1 -0
  132. package/src/versioning.ts +8 -1
@@ -44,9 +44,15 @@ export class AztecKVTxPool implements TxPool {
44
44
  /** The cumulative tx size in bytes that the pending txs in the pool take up. */
45
45
  #pendingTxSize: AztecAsyncSingleton<number>;
46
46
 
47
+ /** Count of total pending txs. */
48
+ #pendingTxCount: AztecAsyncSingleton<number>;
49
+
47
50
  /** In-memory mapping of pending tx hashes to the hydrated pending tx in the pool. */
48
51
  #pendingTxs: Map<string, Tx>;
49
52
 
53
+ /** In-memory set of txs that should not be evicted from the pool. */
54
+ #nonEvictableTxs: Set<string>;
55
+
50
56
  /** KV store for archived txs. */
51
57
  #archive: AztecAsyncKVStore;
52
58
 
@@ -91,8 +97,10 @@ export class AztecKVTxPool implements TxPool {
91
97
  this.#pendingTxHashToSize = store.openMap('pendingTxHashToSize');
92
98
  this.#pendingTxHashToHeaderHash = store.openMap('pendingTxHashToHeaderHash');
93
99
  this.#pendingTxSize = store.openSingleton('pendingTxSize');
100
+ this.#pendingTxCount = store.openSingleton('pendingTxCount');
94
101
  this.#maxTxPoolSize = config.maxTxPoolSize;
95
102
  this.#pendingTxs = new Map<string, Tx>();
103
+ this.#nonEvictableTxs = new Set<string>();
96
104
 
97
105
  this.#archivedTxs = archive.openMap('archivedTxs');
98
106
  this.#archivedTxIndices = archive.openMap('archivedTxIndices');
@@ -112,7 +120,7 @@ export class AztecKVTxPool implements TxPool {
112
120
  return true;
113
121
  }
114
122
 
115
- public markAsMined(txHashes: TxHash[], blockNumber: number): Promise<void> {
123
+ public async markAsMined(txHashes: TxHash[], blockNumber: number): Promise<void> {
116
124
  if (txHashes.length === 0) {
117
125
  return Promise.resolve();
118
126
  }
@@ -120,7 +128,8 @@ export class AztecKVTxPool implements TxPool {
120
128
  let deletedPending = 0;
121
129
  const minedNullifiers = new Set<string>();
122
130
  const minedFeePayers = new Set<string>();
123
- return this.#store.transactionAsync(async () => {
131
+
132
+ await this.#store.transactionAsync(async () => {
124
133
  let pendingTxSize = (await this.#pendingTxSize.getAsync()) ?? 0;
125
134
  for (const hash of txHashes) {
126
135
  const key = hash.toString();
@@ -139,6 +148,7 @@ export class AztecKVTxPool implements TxPool {
139
148
  }
140
149
  this.#metrics.recordAddedObjects(txHashes.length, 'mined');
141
150
  await this.#pendingTxSize.set(pendingTxSize);
151
+ await this.increasePendingTxCount(-deletedPending);
142
152
 
143
153
  const numTxsEvicted = await this.evictInvalidTxsAfterMining(
144
154
  txHashes,
@@ -148,43 +158,44 @@ export class AztecKVTxPool implements TxPool {
148
158
  );
149
159
  this.#metrics.recordRemovedObjects(deletedPending + numTxsEvicted, 'pending');
150
160
  });
161
+ // We update this after the transaction above. This ensures that the non-evictable transactions are not evicted
162
+ // until any that have been mined are marked as such.
163
+ // The non-evictable set is not considered when evicting transactions that are invalid after a block is mined.
164
+ this.#nonEvictableTxs.clear();
151
165
  }
152
166
 
153
- public markMinedAsPending(txHashes: TxHash[]): Promise<void> {
167
+ public async markMinedAsPending(txHashes: TxHash[]): Promise<void> {
154
168
  if (txHashes.length === 0) {
155
169
  return Promise.resolve();
156
170
  }
157
171
 
158
172
  let markedAsPending = 0;
159
- return this.#store
160
- .transactionAsync(async () => {
161
- let pendingTxSize = (await this.#pendingTxSize.getAsync()) ?? 0;
162
- for (const hash of txHashes) {
163
- const key = hash.toString();
164
- await this.#minedTxHashToBlock.delete(key);
173
+ await this.#store.transactionAsync(async () => {
174
+ let pendingTxSize = (await this.#pendingTxSize.getAsync()) ?? 0;
175
+ for (const hash of txHashes) {
176
+ const key = hash.toString();
177
+ await this.#minedTxHashToBlock.delete(key);
165
178
 
166
- // Rehydrate the tx in the in-memory pending txs mapping
167
- const tx = await this.getPendingTxByHash(hash);
168
- if (tx) {
169
- await this.addPendingTxIndices(tx, key);
170
- pendingTxSize += tx.getSize();
171
- markedAsPending++;
172
- }
179
+ // Rehydrate the tx in the in-memory pending txs mapping
180
+ const tx = await this.getPendingTxByHash(hash);
181
+ if (tx) {
182
+ await this.addPendingTxIndices(tx, key);
183
+ pendingTxSize += tx.getSize();
184
+ markedAsPending++;
173
185
  }
186
+ }
187
+
188
+ await this.#pendingTxSize.set(pendingTxSize);
189
+ });
174
190
 
175
- await this.#pendingTxSize.set(pendingTxSize);
176
- })
177
- .then(async () => {
178
- const numInvalidTxsEvicted = await this.evictInvalidTxsAfterReorg(txHashes);
179
- const { numLowPriorityTxsEvicted, numNewTxsEvicted } = await this.evictLowPriorityTxs(txHashes);
180
-
181
- this.#metrics.recordAddedObjects(markedAsPending - numNewTxsEvicted, 'pending');
182
- this.#metrics.recordRemovedObjects(
183
- numInvalidTxsEvicted + numLowPriorityTxsEvicted - numNewTxsEvicted,
184
- 'pending',
185
- );
186
- this.#metrics.recordRemovedObjects(markedAsPending, 'mined');
187
- });
191
+ const numInvalidTxsEvicted = await this.evictInvalidTxsAfterReorg(txHashes);
192
+ const { numLowPriorityTxsEvicted, numNewTxsEvicted } = await this.evictLowPriorityTxs(txHashes);
193
+
194
+ await this.increasePendingTxCount(markedAsPending);
195
+
196
+ this.#metrics.recordAddedObjects(markedAsPending - numNewTxsEvicted, 'pending');
197
+ this.#metrics.recordRemovedObjects(numInvalidTxsEvicted + numLowPriorityTxsEvicted - numNewTxsEvicted, 'pending');
198
+ this.#metrics.recordRemovedObjects(markedAsPending, 'mined');
188
199
  }
189
200
 
190
201
  public async getPendingTxHashes(): Promise<TxHash[]> {
@@ -197,6 +208,10 @@ export class AztecKVTxPool implements TxPool {
197
208
  return vals.map(([txHash, blockNumber]) => [TxHash.fromString(txHash), blockNumber]);
198
209
  }
199
210
 
211
+ public async getPendingTxCount(): Promise<number> {
212
+ return (await this.#pendingTxCount.getAsync()) ?? 0;
213
+ }
214
+
200
215
  public async getTxStatus(txHash: TxHash): Promise<'pending' | 'mined' | undefined> {
201
216
  const key = txHash.toString();
202
217
  const [isMined, isKnown] = await Promise.all([this.#minedTxHashToBlock.hasAsync(key), this.#txs.hasAsync(key)]);
@@ -266,21 +281,26 @@ export class AztecKVTxPool implements TxPool {
266
281
  txs.map(async tx => ({ txHash: await tx.getTxHash(), txStats: await tx.getStats() })),
267
282
  );
268
283
  await this.#store.transactionAsync(async () => {
269
- let pendingCount = 0;
284
+ let addedCount = 0;
270
285
  let pendingTxSize = (await this.#pendingTxSize.getAsync()) ?? 0;
271
286
  await Promise.all(
272
287
  txs.map(async (tx, i) => {
273
288
  const { txHash, txStats } = hashesAndStats[i];
289
+ const key = txHash.toString();
290
+ if (await this.#txs.hasAsync(key)) {
291
+ this.#log.debug(`Tx ${txHash.toString()} already exists in the pool`);
292
+ return;
293
+ }
294
+
274
295
  this.#log.verbose(`Adding tx ${txHash.toString()} to pool`, {
275
296
  eventName: 'tx-added-to-pool',
276
297
  ...txStats,
277
298
  } satisfies TxAddedToPoolStats);
278
299
 
279
- const key = txHash.toString();
280
300
  await this.#txs.set(key, tx.toBuffer());
281
301
 
282
302
  if (!(await this.#minedTxHashToBlock.hasAsync(key))) {
283
- pendingCount++;
303
+ addedCount++;
284
304
  pendingTxSize += tx.getSize();
285
305
  await this.addPendingTxIndices(tx, key);
286
306
  this.#metrics.recordSize(tx);
@@ -288,13 +308,13 @@ export class AztecKVTxPool implements TxPool {
288
308
  }),
289
309
  );
290
310
 
311
+ await this.increasePendingTxCount(addedCount);
291
312
  await this.#pendingTxSize.set(pendingTxSize);
292
-
293
313
  const { numLowPriorityTxsEvicted, numNewTxsEvicted } = await this.evictLowPriorityTxs(
294
314
  hashesAndStats.map(({ txHash }) => txHash),
295
315
  );
296
316
 
297
- this.#metrics.recordAddedObjects(pendingCount - numNewTxsEvicted, 'pending');
317
+ this.#metrics.recordAddedObjects(addedCount - numNewTxsEvicted, 'pending');
298
318
  this.#metrics.recordRemovedObjects(numLowPriorityTxsEvicted - numNewTxsEvicted, 'pending');
299
319
  });
300
320
  }
@@ -335,6 +355,8 @@ export class AztecKVTxPool implements TxPool {
335
355
  }
336
356
 
337
357
  await this.#pendingTxSize.set(pendingTxSize);
358
+ await this.increasePendingTxCount(-pendingDeleted);
359
+
338
360
  this.#metrics.recordRemovedObjects(pendingDeleted, 'pending');
339
361
  this.#metrics.recordRemovedObjects(minedDeleted, 'mined');
340
362
  });
@@ -369,6 +391,11 @@ export class AztecKVTxPool implements TxPool {
369
391
  return Promise.resolve();
370
392
  }
371
393
 
394
+ public markTxsAsNonEvictable(txHashes: TxHash[]): Promise<void> {
395
+ txHashes.forEach(txHash => this.#nonEvictableTxs.add(txHash.toString()));
396
+ return Promise.resolve();
397
+ }
398
+
372
399
  /**
373
400
  * Creates a GasTxValidator instance.
374
401
  * @param db - DB for the validator to use
@@ -466,6 +493,9 @@ export class AztecKVTxPool implements TxPool {
466
493
  let pendingTxsSize = (await this.#pendingTxSize.getAsync()) ?? 0;
467
494
  if (pendingTxsSize > this.#maxTxPoolSize) {
468
495
  for await (const txHash of this.#pendingTxPriorityToHash.valuesAsync()) {
496
+ if (this.#nonEvictableTxs.has(txHash.toString())) {
497
+ continue;
498
+ }
469
499
  this.#log.verbose(`Evicting tx ${txHash} from pool due to low priority to satisfy max tx size limit`);
470
500
  txsToEvict.push(TxHash.fromString(txHash));
471
501
 
@@ -613,4 +643,12 @@ export class AztecKVTxPool implements TxPool {
613
643
  await this.#pendingTxHashToHeaderHash.delete(txHash);
614
644
  this.#pendingTxs.delete(txHash);
615
645
  }
646
+
647
+ private async increasePendingTxCount(count: number): Promise<void> {
648
+ const pendingTxCount = (await this.#pendingTxCount.getAsync()) ?? 0;
649
+ this.#log.debug(
650
+ `Increasing pending tx count: current ${pendingTxCount} + count ${count} = ${pendingTxCount + count}`,
651
+ );
652
+ await this.#pendingTxCount.set(pendingTxCount + count);
653
+ }
616
654
  }
@@ -24,7 +24,10 @@ export class InMemoryTxPool implements TxPool {
24
24
  * Class constructor for in-memory TxPool. Initiates our transaction pool as a JS Map.
25
25
  * @param log - A logger.
26
26
  */
27
- constructor(telemetry: TelemetryClient = getTelemetryClient(), private log = createLogger('p2p:tx_pool')) {
27
+ constructor(
28
+ telemetry: TelemetryClient = getTelemetryClient(),
29
+ private log = createLogger('p2p:tx_pool'),
30
+ ) {
28
31
  this.txs = new Map<bigint, Tx>();
29
32
  this.minedTxs = new Map();
30
33
  this.pendingTxs = new Set();
@@ -86,6 +89,10 @@ export class InMemoryTxPool implements TxPool {
86
89
  );
87
90
  }
88
91
 
92
+ public getPendingTxCount(): Promise<number> {
93
+ return Promise.resolve(this.pendingTxs.size);
94
+ }
95
+
89
96
  public getTxStatus(txHash: TxHash): Promise<'pending' | 'mined' | undefined> {
90
97
  const key = txHash.toBigInt();
91
98
  if (this.pendingTxs.has(key)) {
@@ -186,4 +193,8 @@ export class InMemoryTxPool implements TxPool {
186
193
  setMaxTxPoolSize(_maxSizeBytes: number | undefined): Promise<void> {
187
194
  return Promise.resolve();
188
195
  }
196
+
197
+ markTxsAsNonEvictable(_: TxHash[]): Promise<void> {
198
+ return Promise.resolve();
199
+ }
189
200
  }
@@ -75,6 +75,9 @@ export interface TxPool {
75
75
  */
76
76
  getPendingTxHashes(): Promise<TxHash[]>;
77
77
 
78
+ /** Returns the number of pending txs in the pool. */
79
+ getPendingTxCount(): Promise<number>;
80
+
78
81
  /**
79
82
  * Gets the hashes of mined transactions currently in the tx pool.
80
83
  * @returns An array of mined transaction hashes found in the tx pool.
@@ -96,4 +99,10 @@ export interface TxPool {
96
99
 
97
100
  /** Returns whether the pool is empty. */
98
101
  isEmpty(): Promise<boolean>;
102
+
103
+ /**
104
+ * Marks transactions as non-evictible in the pool.
105
+ * @param txHashes - Hashes of the transactions to mark as non-evictible.
106
+ */
107
+ markTxsAsNonEvictable(txHashes: TxHash[]): Promise<void>;
99
108
  }
@@ -24,6 +24,7 @@ export function describeTxPool(getTxPool: () => TxPool) {
24
24
  expect(await poolTx!.getTxHash()).toEqual(await tx1.getTxHash());
25
25
  await expect(pool.getTxStatus(await tx1.getTxHash())).resolves.toEqual('pending');
26
26
  await expect(pool.getPendingTxHashes()).resolves.toEqual([await tx1.getTxHash()]);
27
+ await expect(pool.getPendingTxCount()).resolves.toEqual(1);
27
28
  });
28
29
 
29
30
  it('Removes txs from the pool', async () => {
@@ -34,6 +35,7 @@ export function describeTxPool(getTxPool: () => TxPool) {
34
35
 
35
36
  await expect(pool.getTxByHash(await tx1.getTxHash())).resolves.toBeFalsy();
36
37
  await expect(pool.getTxStatus(await tx1.getTxHash())).resolves.toBeUndefined();
38
+ await expect(pool.getPendingTxCount()).resolves.toEqual(0);
37
39
  });
38
40
 
39
41
  it('Marks txs as mined', async () => {
@@ -47,6 +49,7 @@ export function describeTxPool(getTxPool: () => TxPool) {
47
49
  await expect(pool.getTxStatus(await tx1.getTxHash())).resolves.toEqual('mined');
48
50
  await expect(pool.getMinedTxHashes()).resolves.toEqual([[await tx1.getTxHash(), 1]]);
49
51
  await expect(pool.getPendingTxHashes()).resolves.toEqual([await tx2.getTxHash()]);
52
+ await expect(pool.getPendingTxCount()).resolves.toEqual(1);
50
53
  });
51
54
 
52
55
  it('Marks txs as pending after being mined', async () => {
@@ -61,6 +64,7 @@ export function describeTxPool(getTxPool: () => TxPool) {
61
64
  const pending = await pool.getPendingTxHashes();
62
65
  expect(pending).toHaveLength(2);
63
66
  expect(pending).toEqual(expect.arrayContaining([await tx1.getTxHash(), await tx2.getTxHash()]));
67
+ await expect(pool.getPendingTxCount()).resolves.toEqual(2);
64
68
  });
65
69
 
66
70
  it('Only marks txs as pending if they are known', async () => {
@@ -82,6 +86,7 @@ export function describeTxPool(getTxPool: () => TxPool) {
82
86
  await pool.markMinedAsPending([await tx1.getTxHash(), someTxHashThatThisPeerDidNotSee]);
83
87
  await expect(pool.getMinedTxHashes()).resolves.toEqual([]);
84
88
  await expect(pool.getPendingTxHashes()).resolves.toEqual([await tx1.getTxHash()]); // tx2 is not in the pool
89
+ await expect(pool.getPendingTxCount()).resolves.toEqual(1);
85
90
  });
86
91
 
87
92
  it('Returns all transactions in the pool', async () => {
@@ -94,6 +99,7 @@ export function describeTxPool(getTxPool: () => TxPool) {
94
99
  const poolTxs = await pool.getAllTxs();
95
100
  expect(poolTxs).toHaveLength(3);
96
101
  expect(poolTxs).toEqual(expect.arrayContaining([tx1, tx2, tx3]));
102
+ await expect(pool.getPendingTxCount()).resolves.toEqual(3);
97
103
  });
98
104
 
99
105
  it('Returns all txHashes in the pool', async () => {
@@ -104,10 +110,10 @@ export function describeTxPool(getTxPool: () => TxPool) {
104
110
  await pool.addTxs([tx1, tx2, tx3]);
105
111
 
106
112
  const poolTxHashes = await pool.getAllTxHashes();
113
+ const expectedHashes = await Promise.all([tx1, tx2, tx3].map(tx => tx.getTxHash()));
107
114
  expect(poolTxHashes).toHaveLength(3);
108
- expect(poolTxHashes).toEqual(
109
- expect.arrayContaining([await tx1.getTxHash(), await tx2.getTxHash(), await tx3.getTxHash()]),
110
- );
115
+ expect(poolTxHashes).toEqual(expect.arrayContaining(expectedHashes));
116
+ await expect(pool.getPendingTxCount()).resolves.toEqual(3);
111
117
  });
112
118
 
113
119
  it('Returns txs by their hash', async () => {
@@ -17,7 +17,7 @@ export class BlockHeaderTxValidator<T extends AnyTx> implements TxValidator<T> {
17
17
  async validateTx(tx: T): Promise<TxValidationResult> {
18
18
  const [index] = await this.#archiveSource.getArchiveIndices([await tx.data.constants.historicalHeader.hash()]);
19
19
  if (index === undefined) {
20
- this.#log.warn(`Rejecting tx ${await Tx.getHash(tx)} for referencing an unknown block header`);
20
+ this.#log.verbose(`Rejecting tx ${await Tx.getHash(tx)} for referencing an unknown block header`);
21
21
  return { result: 'invalid', reason: [TX_ERROR_BLOCK_HEADER] };
22
22
  }
23
23
  return { result: 'valid' };
@@ -26,7 +26,7 @@ export class DataTxValidator implements TxValidator<Tx> {
26
26
  async #hasCorrectCalldata(tx: Tx): Promise<TxValidationResult> {
27
27
  if (tx.publicFunctionCalldata.length !== tx.numberOfPublicCalls()) {
28
28
  const reason = TX_ERROR_CALLDATA_COUNT_MISMATCH;
29
- this.#log.warn(
29
+ this.#log.verbose(
30
30
  `Rejecting tx ${await Tx.getHash(tx)}. Reason: ${reason}. Expected ${tx.numberOfPublicCalls()}. Got ${
31
31
  tx.publicFunctionCalldata.length
32
32
  }.`,
@@ -36,7 +36,7 @@ export class DataTxValidator implements TxValidator<Tx> {
36
36
 
37
37
  if (tx.getTotalPublicCalldataCount() > MAX_FR_CALLDATA_TO_ALL_ENQUEUED_CALLS) {
38
38
  const reason = TX_ERROR_CALLDATA_COUNT_TOO_LARGE;
39
- this.#log.warn(
39
+ this.#log.verbose(
40
40
  `Rejecting tx ${await Tx.getHash(
41
41
  tx,
42
42
  )}. Reason: ${reason}. Expected no greater than ${MAX_FR_CALLDATA_TO_ALL_ENQUEUED_CALLS} fields. Got ${tx.getTotalPublicCalldataCount()}.`,
@@ -50,7 +50,7 @@ export class DataTxValidator implements TxValidator<Tx> {
50
50
  const hash = await computeCalldataHash(calldata);
51
51
  if (!hash.equals(request.calldataHash)) {
52
52
  const reason = TX_ERROR_INCORRECT_CALLDATA;
53
- this.#log.warn(`Rejecting tx ${await Tx.getHash(tx)}. Reason: ${reason}. Call request index: ${i}.`);
53
+ this.#log.verbose(`Rejecting tx ${await Tx.getHash(tx)}. Reason: ${reason}. Call request index: ${i}.`);
54
54
  return { result: 'invalid', reason: [reason] };
55
55
  }
56
56
  }
@@ -60,44 +60,50 @@ export class DataTxValidator implements TxValidator<Tx> {
60
60
 
61
61
  async #hasCorrectContractClassLogs(tx: Tx): Promise<TxValidationResult> {
62
62
  const contractClassLogsHashes = tx.data.getNonEmptyContractClassLogsHashes();
63
- const hashedContractClasslogs = await Promise.all(tx.contractClassLogs.map(l => l.hash()));
64
- if (contractClassLogsHashes.length !== hashedContractClasslogs.length) {
65
- this.#log.warn(
63
+ if (contractClassLogsHashes.length !== tx.contractClassLogs.length) {
64
+ this.#log.verbose(
66
65
  `Rejecting tx ${await Tx.getHash(tx)} because of mismatched number of contract class logs. Expected ${
67
66
  contractClassLogsHashes.length
68
- }. Got ${hashedContractClasslogs.length}.`,
67
+ }. Got ${tx.contractClassLogs.length}.`,
69
68
  );
70
69
  return { result: 'invalid', reason: [TX_ERROR_CONTRACT_CLASS_LOG_COUNT] };
71
70
  }
71
+
72
+ const expectedHashes = await Promise.all(tx.contractClassLogs.map(l => l.hash()));
72
73
  for (const [i, logHash] of contractClassLogsHashes.entries()) {
73
- const hashedLog = hashedContractClasslogs[i];
74
- if (!logHash.value.equals(hashedLog)) {
75
- if (hashedContractClasslogs.some(l => logHash.value.equals(l))) {
76
- const matchingLogIndex = hashedContractClasslogs.findIndex(l => logHash.value.equals(l));
77
- this.#log.warn(
74
+ const hash = expectedHashes[i];
75
+ if (!logHash.value.equals(hash)) {
76
+ if (expectedHashes.some(h => logHash.value.equals(h))) {
77
+ const matchingLogIndex = expectedHashes.findIndex(l => logHash.value.equals(l));
78
+ this.#log.verbose(
78
79
  `Rejecting tx ${await Tx.getHash(
79
80
  tx,
80
81
  )} because of mismatched contract class logs indices. Expected ${i} from the kernel's log hashes. Got ${matchingLogIndex} in the tx.`,
81
82
  );
82
83
  return { result: 'invalid', reason: [TX_ERROR_CONTRACT_CLASS_LOG_SORTING] };
83
84
  } else {
84
- this.#log.warn(
85
+ this.#log.verbose(
85
86
  `Rejecting tx ${await Tx.getHash(tx)} because of mismatched contract class logs. Expected hash ${
86
87
  logHash.value
87
- } from the kernels. Got ${hashedLog} in the tx.`,
88
+ } from the kernels. Got ${hash} in the tx.`,
88
89
  );
89
90
  return { result: 'invalid', reason: [TX_ERROR_CONTRACT_CLASS_LOGS] };
90
91
  }
91
92
  }
92
- if (logHash.logHash.length !== tx.contractClassLogs[i].getEmittedLength()) {
93
- this.#log.warn(
94
- `Rejecting tx ${await Tx.getHash(tx)} because of mismatched contract class logs length. Expected ${
93
+
94
+ const expectedMinLength = 1 + tx.contractClassLogs[i].fields.findLastIndex(f => !f.isZero());
95
+ if (logHash.logHash.length < expectedMinLength) {
96
+ this.#log.verbose(
97
+ `Rejecting tx ${await Tx.getHash(
98
+ tx,
99
+ )} because of incorrect contract class log length. Expected the length to be at least ${expectedMinLength}. Got ${
95
100
  logHash.logHash.length
96
- } from the kernel's log hashes. Got ${tx.contractClassLogs[i].getEmittedLength()} in the tx.`,
101
+ }.`,
97
102
  );
98
103
  return { result: 'invalid', reason: [TX_ERROR_CONTRACT_CLASS_LOG_LENGTH] };
99
104
  }
100
105
  }
106
+
101
107
  return { result: 'valid' };
102
108
  }
103
109
  }
@@ -26,12 +26,12 @@ export class DoubleSpendTxValidator<T extends AnyTx> implements TxValidator<T> {
26
26
  // Ditch this tx if it has repeated nullifiers
27
27
  const uniqueNullifiers = new Set(nullifiers);
28
28
  if (uniqueNullifiers.size !== nullifiers.length) {
29
- this.#log.warn(`Rejecting tx ${await Tx.getHash(tx)} for emitting duplicate nullifiers`);
29
+ this.#log.verbose(`Rejecting tx ${await Tx.getHash(tx)} for emitting duplicate nullifiers`);
30
30
  return { result: 'invalid', reason: [TX_ERROR_DUPLICATE_NULLIFIER_IN_TX] };
31
31
  }
32
32
 
33
33
  if ((await this.#nullifierSource.nullifiersExist(nullifiers.map(n => n.toBuffer()))).some(Boolean)) {
34
- this.#log.warn(`Rejecting tx ${await Tx.getHash(tx)} for repeating a nullifier`);
34
+ this.#log.verbose(`Rejecting tx ${await Tx.getHash(tx)} for repeating a nullifier`);
35
35
  return { result: 'invalid', reason: [TX_ERROR_EXISTING_NULLIFIER] };
36
36
  }
37
37
 
@@ -0,0 +1,94 @@
1
+ import { Fr } from '@aztec/foundation/fields';
2
+ import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
3
+ import { ProtocolContractAddress } from '@aztec/protocol-contracts';
4
+ import type { ContractDataSource } from '@aztec/stdlib/contract';
5
+ import type { GasFees } from '@aztec/stdlib/gas';
6
+ import type {
7
+ AllowedElement,
8
+ ClientProtocolCircuitVerifier,
9
+ WorldStateSynchronizer,
10
+ } from '@aztec/stdlib/interfaces/server';
11
+ import { PeerErrorSeverity } from '@aztec/stdlib/p2p';
12
+ import { DatabasePublicStateSource, MerkleTreeId } from '@aztec/stdlib/trees';
13
+ import type { Tx, TxValidationResult } from '@aztec/stdlib/tx';
14
+
15
+ import { ArchiveCache } from './archive_cache.js';
16
+ import { BlockHeaderTxValidator } from './block_header_validator.js';
17
+ import { DataTxValidator } from './data_validator.js';
18
+ import { DoubleSpendTxValidator } from './double_spend_validator.js';
19
+ import { GasTxValidator } from './gas_validator.js';
20
+ import { MetadataTxValidator } from './metadata_validator.js';
21
+ import { PhasesTxValidator } from './phases_validator.js';
22
+ import { TxProofValidator } from './tx_proof_validator.js';
23
+
24
+ export interface MessageValidator {
25
+ validator: {
26
+ validateTx(tx: Tx): Promise<TxValidationResult>;
27
+ };
28
+ severity: PeerErrorSeverity;
29
+ }
30
+
31
+ export function createTxMessageValidators(
32
+ blockNumber: number,
33
+ worldStateSynchronizer: WorldStateSynchronizer,
34
+ gasFees: GasFees,
35
+ l1ChainId: number,
36
+ rollupVersion: number,
37
+ protocolContractTreeRoot: Fr,
38
+ contractDataSource: ContractDataSource,
39
+ proofVerifier: ClientProtocolCircuitVerifier,
40
+ allowedInSetup: AllowedElement[] = [],
41
+ ): Record<string, MessageValidator>[] {
42
+ const merkleTree = worldStateSynchronizer.getCommitted();
43
+
44
+ return [
45
+ {
46
+ dataValidator: {
47
+ validator: new DataTxValidator(),
48
+ severity: PeerErrorSeverity.HighToleranceError,
49
+ },
50
+ metadataValidator: {
51
+ validator: new MetadataTxValidator({
52
+ l1ChainId: new Fr(l1ChainId),
53
+ rollupVersion: new Fr(rollupVersion),
54
+ blockNumber: new Fr(blockNumber),
55
+ protocolContractTreeRoot,
56
+ vkTreeRoot: getVKTreeRoot(),
57
+ }),
58
+ severity: PeerErrorSeverity.HighToleranceError,
59
+ },
60
+ doubleSpendValidator: {
61
+ validator: new DoubleSpendTxValidator({
62
+ nullifiersExist: async (nullifiers: Buffer[]) => {
63
+ const merkleTree = worldStateSynchronizer.getCommitted();
64
+ const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
65
+ return indices.map(index => index !== undefined);
66
+ },
67
+ }),
68
+ severity: PeerErrorSeverity.HighToleranceError,
69
+ },
70
+ gasValidator: {
71
+ validator: new GasTxValidator(
72
+ new DatabasePublicStateSource(merkleTree),
73
+ ProtocolContractAddress.FeeJuice,
74
+ gasFees,
75
+ ),
76
+ severity: PeerErrorSeverity.HighToleranceError,
77
+ },
78
+ phasesValidator: {
79
+ validator: new PhasesTxValidator(contractDataSource, allowedInSetup, blockNumber),
80
+ severity: PeerErrorSeverity.MidToleranceError,
81
+ },
82
+ blockHeaderValidator: {
83
+ validator: new BlockHeaderTxValidator(new ArchiveCache(merkleTree)),
84
+ severity: PeerErrorSeverity.HighToleranceError,
85
+ },
86
+ },
87
+ {
88
+ proofValidator: {
89
+ validator: new TxProofValidator(proofVerifier),
90
+ severity: PeerErrorSeverity.MidToleranceError,
91
+ },
92
+ },
93
+ ];
94
+ }
@@ -55,7 +55,7 @@ export class GasTxValidator implements TxValidator<Tx> {
55
55
  maxFeesPerGas.feePerL2Gas.lt(this.#gasFees.feePerL2Gas);
56
56
 
57
57
  if (notEnoughMaxFees) {
58
- this.#log.warn(`Skipping transaction ${await tx.getTxHash()} due to insufficient fee per gas`, {
58
+ this.#log.verbose(`Skipping transaction ${await tx.getTxHash()} due to insufficient fee per gas`, {
59
59
  txMaxFeesPerGas: maxFeesPerGas.toInspect(),
60
60
  currentGasFees: this.#gasFees.toInspect(),
61
61
  });
@@ -71,7 +71,7 @@ export class GasTxValidator implements TxValidator<Tx> {
71
71
  const minGasLimits = new Gas(FIXED_DA_GAS, FIXED_L2_GAS);
72
72
 
73
73
  if (minGasLimits.gtAny(gasLimits)) {
74
- this.#log.warn(`Rejecting transaction due to the gas limit(s) not being above the minimum gas limit`, {
74
+ this.#log.verbose(`Rejecting transaction due to the gas limit(s) not being above the minimum gas limit`, {
75
75
  gasLimits,
76
76
  minGasLimits,
77
77
  });
@@ -113,7 +113,7 @@ export class GasTxValidator implements TxValidator<Tx> {
113
113
 
114
114
  const balance = claimFunctionCall ? initialBalance.add(claimFunctionCall.args[1]) : initialBalance;
115
115
  if (balance.lt(feeLimit)) {
116
- this.#log.warn(`Rejecting transaction due to not enough fee payer balance`, {
116
+ this.#log.verbose(`Rejecting transaction due to not enough fee payer balance`, {
117
117
  feePayer,
118
118
  balance: balance.toBigInt(),
119
119
  feeLimit: feeLimit.toBigInt(),
@@ -42,7 +42,7 @@ export class MetadataTxValidator<T extends AnyTx> implements TxValidator<T> {
42
42
  async #hasCorrectVkTreeRoot(tx: T): Promise<boolean> {
43
43
  // This gets implicitly tested in the proof validator, but we can get a much cheaper check here by looking early at the vk.
44
44
  if (!tx.data.constants.vkTreeRoot.equals(this.values.vkTreeRoot)) {
45
- this.#log.warn(
45
+ this.#log.verbose(
46
46
  `Rejecting tx ${await Tx.getHash(
47
47
  tx,
48
48
  )} because of incorrect vk tree root ${tx.data.constants.vkTreeRoot.toString()} != ${this.values.vkTreeRoot.toString()}`,
@@ -55,7 +55,7 @@ export class MetadataTxValidator<T extends AnyTx> implements TxValidator<T> {
55
55
 
56
56
  async #hasCorrectProtocolContractTreeRoot(tx: T): Promise<boolean> {
57
57
  if (!tx.data.constants.protocolContractTreeRoot.equals(this.values.protocolContractTreeRoot)) {
58
- this.#log.warn(
58
+ this.#log.verbose(
59
59
  `Rejecting tx ${await Tx.getHash(
60
60
  tx,
61
61
  )} because of incorrect protocol contract tree root ${tx.data.constants.protocolContractTreeRoot.toString()} != ${this.values.protocolContractTreeRoot.toString()}`,
@@ -67,7 +67,7 @@ export class MetadataTxValidator<T extends AnyTx> implements TxValidator<T> {
67
67
 
68
68
  async #hasCorrectL1ChainId(tx: T): Promise<boolean> {
69
69
  if (!tx.data.constants.txContext.chainId.equals(this.values.l1ChainId)) {
70
- this.#log.warn(
70
+ this.#log.verbose(
71
71
  `Rejecting tx ${await Tx.getHash(
72
72
  tx,
73
73
  )} because of incorrect L1 chain ${tx.data.constants.txContext.chainId.toNumber()} != ${this.values.l1ChainId.toNumber()}`,
@@ -82,7 +82,7 @@ export class MetadataTxValidator<T extends AnyTx> implements TxValidator<T> {
82
82
  const maxBlockNumber = tx.data.rollupValidationRequests.maxBlockNumber;
83
83
 
84
84
  if (maxBlockNumber.isSome && maxBlockNumber.value < this.values.blockNumber) {
85
- this.#log.warn(
85
+ this.#log.verbose(
86
86
  `Rejecting tx ${await Tx.getHash(tx)} for low max block number. Tx max block number: ${
87
87
  maxBlockNumber.value
88
88
  }, current block number: ${this.values.blockNumber}.`,
@@ -95,7 +95,7 @@ export class MetadataTxValidator<T extends AnyTx> implements TxValidator<T> {
95
95
 
96
96
  async #hasCorrectRollupVersion(tx: T): Promise<boolean> {
97
97
  if (!tx.data.constants.txContext.version.equals(this.values.rollupVersion)) {
98
- this.#log.warn(
98
+ this.#log.verbose(
99
99
  `Rejecting tx ${await Tx.getHash(
100
100
  tx,
101
101
  )} because of incorrect rollup version ${tx.data.constants.txContext.version.toNumber()} != ${this.values.rollupVersion.toNumber()}`,
@@ -16,7 +16,11 @@ export class PhasesTxValidator implements TxValidator<Tx> {
16
16
  #log = createLogger('sequencer:tx_validator:tx_phases');
17
17
  private contractsDB: PublicContractsDB;
18
18
 
19
- constructor(contracts: ContractDataSource, private setupAllowList: AllowedElement[], private blockNumber: number) {
19
+ constructor(
20
+ contracts: ContractDataSource,
21
+ private setupAllowList: AllowedElement[],
22
+ private blockNumber: number,
23
+ ) {
20
24
  this.contractsDB = new PublicContractsDB(contracts);
21
25
  }
22
26
 
@@ -37,7 +41,7 @@ export class PhasesTxValidator implements TxValidator<Tx> {
37
41
  const setupFns = getCallRequestsWithCalldataByPhase(tx, TxExecutionPhase.SETUP);
38
42
  for (const setupFn of setupFns) {
39
43
  if (!(await this.isOnAllowList(setupFn, this.setupAllowList))) {
40
- this.#log.warn(
44
+ this.#log.verbose(
41
45
  `Rejecting tx ${await Tx.getHash(tx)} because it calls setup function not on allow list: ${
42
46
  setupFn.request.contractAddress
43
47
  }:${setupFn.functionSelector}`,
@@ -9,7 +9,7 @@ export class TxProofValidator implements TxValidator<Tx> {
9
9
 
10
10
  async validateTx(tx: Tx): Promise<TxValidationResult> {
11
11
  if (!(await this.verifier.verifyProof(tx))) {
12
- this.#log.warn(`Rejecting tx ${await Tx.getHash(tx)} for invalid proof`);
12
+ this.#log.verbose(`Rejecting tx ${await Tx.getHash(tx)} for invalid proof`);
13
13
  return { result: 'invalid', reason: [TX_ERROR_INVALID_PROOF] };
14
14
  }
15
15
  this.#log.trace(`Accepted ${await Tx.getHash(tx)} with valid proof`);