@aztec/p2p 0.0.1-commit.6d63667d → 0.0.1-commit.7cf39cb55

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 (220) hide show
  1. package/dest/client/factory.d.ts +5 -5
  2. package/dest/client/factory.d.ts.map +1 -1
  3. package/dest/client/factory.js +44 -10
  4. package/dest/client/interface.d.ts +37 -15
  5. package/dest/client/interface.d.ts.map +1 -1
  6. package/dest/client/p2p_client.d.ts +35 -36
  7. package/dest/client/p2p_client.d.ts.map +1 -1
  8. package/dest/client/p2p_client.js +114 -138
  9. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +1 -1
  10. package/dest/config.d.ts +22 -4
  11. package/dest/config.d.ts.map +1 -1
  12. package/dest/config.js +16 -1
  13. package/dest/index.d.ts +2 -1
  14. package/dest/index.d.ts.map +1 -1
  15. package/dest/index.js +1 -0
  16. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +104 -88
  17. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  18. package/dest/mem_pools/attestation_pool/attestation_pool.js +441 -3
  19. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +2 -2
  20. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  21. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +353 -87
  22. package/dest/mem_pools/attestation_pool/index.d.ts +2 -3
  23. package/dest/mem_pools/attestation_pool/index.d.ts.map +1 -1
  24. package/dest/mem_pools/attestation_pool/index.js +1 -2
  25. package/dest/mem_pools/index.d.ts +3 -2
  26. package/dest/mem_pools/index.d.ts.map +1 -1
  27. package/dest/mem_pools/index.js +1 -1
  28. package/dest/mem_pools/interface.d.ts +5 -5
  29. package/dest/mem_pools/interface.d.ts.map +1 -1
  30. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +102 -0
  31. package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -0
  32. package/dest/mem_pools/tx_pool_v2/deleted_pool.js +242 -0
  33. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts +1 -1
  34. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -1
  35. package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +3 -0
  36. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.js +3 -1
  37. package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +3 -1
  38. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts +1 -1
  39. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts.map +1 -1
  40. package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.js +3 -1
  41. package/dest/mem_pools/tx_pool_v2/index.d.ts +2 -1
  42. package/dest/mem_pools/tx_pool_v2/index.d.ts.map +1 -1
  43. package/dest/mem_pools/tx_pool_v2/index.js +1 -0
  44. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +7 -3
  45. package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
  46. package/dest/mem_pools/tx_pool_v2/interfaces.js +2 -1
  47. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +27 -3
  48. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
  49. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +37 -4
  50. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +8 -2
  51. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -1
  52. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +15 -2
  53. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +4 -2
  54. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
  55. package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +6 -2
  56. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +4 -2
  57. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
  58. package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +110 -78
  59. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +3 -3
  60. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
  61. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts +3 -3
  62. package/dest/msg_validators/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
  63. package/dest/msg_validators/tx_validator/block_header_validator.d.ts +16 -3
  64. package/dest/msg_validators/tx_validator/block_header_validator.d.ts.map +1 -1
  65. package/dest/msg_validators/tx_validator/block_header_validator.js +1 -1
  66. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts +13 -3
  67. package/dest/msg_validators/tx_validator/double_spend_validator.d.ts.map +1 -1
  68. package/dest/msg_validators/tx_validator/double_spend_validator.js +4 -4
  69. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts +20 -4
  70. package/dest/msg_validators/tx_validator/timestamp_validator.d.ts.map +1 -1
  71. package/dest/msg_validators/tx_validator/timestamp_validator.js +2 -2
  72. package/dest/services/dummy_service.d.ts +10 -2
  73. package/dest/services/dummy_service.d.ts.map +1 -1
  74. package/dest/services/dummy_service.js +6 -0
  75. package/dest/services/encoding.d.ts +2 -2
  76. package/dest/services/encoding.d.ts.map +1 -1
  77. package/dest/services/encoding.js +2 -2
  78. package/dest/services/gossipsub/index.d.ts +3 -0
  79. package/dest/services/gossipsub/index.d.ts.map +1 -0
  80. package/dest/services/gossipsub/index.js +2 -0
  81. package/dest/services/gossipsub/scoring.d.ts +21 -3
  82. package/dest/services/gossipsub/scoring.d.ts.map +1 -1
  83. package/dest/services/gossipsub/scoring.js +24 -7
  84. package/dest/services/gossipsub/topic_score_params.d.ts +161 -0
  85. package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -0
  86. package/dest/services/gossipsub/topic_score_params.js +324 -0
  87. package/dest/services/libp2p/libp2p_service.d.ts +84 -35
  88. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  89. package/dest/services/libp2p/libp2p_service.js +364 -265
  90. package/dest/services/peer-manager/peer_scoring.d.ts +1 -1
  91. package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
  92. package/dest/services/peer-manager/peer_scoring.js +25 -2
  93. package/dest/services/reqresp/interface.d.ts +10 -1
  94. package/dest/services/reqresp/interface.d.ts.map +1 -1
  95. package/dest/services/reqresp/interface.js +15 -1
  96. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +4 -3
  97. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
  98. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +7 -1
  99. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
  100. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +15 -0
  101. package/dest/services/reqresp/protocols/tx.d.ts +7 -1
  102. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
  103. package/dest/services/reqresp/protocols/tx.js +20 -0
  104. package/dest/services/reqresp/reqresp.d.ts +1 -1
  105. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  106. package/dest/services/reqresp/reqresp.js +11 -4
  107. package/dest/services/service.d.ts +35 -1
  108. package/dest/services/service.d.ts.map +1 -1
  109. package/dest/services/tx_collection/config.d.ts +19 -1
  110. package/dest/services/tx_collection/config.d.ts.map +1 -1
  111. package/dest/services/tx_collection/config.js +46 -0
  112. package/dest/services/tx_collection/fast_tx_collection.d.ts +3 -1
  113. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
  114. package/dest/services/tx_collection/fast_tx_collection.js +17 -3
  115. package/dest/services/tx_collection/file_store_tx_collection.d.ts +53 -0
  116. package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -0
  117. package/dest/services/tx_collection/file_store_tx_collection.js +165 -0
  118. package/dest/services/tx_collection/file_store_tx_source.d.ts +28 -0
  119. package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -0
  120. package/dest/services/tx_collection/file_store_tx_source.js +59 -0
  121. package/dest/services/tx_collection/index.d.ts +2 -1
  122. package/dest/services/tx_collection/index.d.ts.map +1 -1
  123. package/dest/services/tx_collection/index.js +1 -0
  124. package/dest/services/tx_collection/slow_tx_collection.d.ts +6 -2
  125. package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -1
  126. package/dest/services/tx_collection/slow_tx_collection.js +55 -23
  127. package/dest/services/tx_collection/tx_collection.d.ts +19 -7
  128. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
  129. package/dest/services/tx_collection/tx_collection.js +75 -3
  130. package/dest/services/tx_collection/tx_collection_sink.d.ts +15 -6
  131. package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
  132. package/dest/services/tx_collection/tx_collection_sink.js +13 -7
  133. package/dest/services/tx_file_store/config.d.ts +1 -3
  134. package/dest/services/tx_file_store/config.d.ts.map +1 -1
  135. package/dest/services/tx_file_store/config.js +0 -4
  136. package/dest/services/tx_file_store/tx_file_store.d.ts +4 -3
  137. package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -1
  138. package/dest/services/tx_file_store/tx_file_store.js +8 -5
  139. package/dest/services/tx_provider.d.ts +3 -3
  140. package/dest/services/tx_provider.d.ts.map +1 -1
  141. package/dest/services/tx_provider.js +5 -4
  142. package/dest/test-helpers/make-test-p2p-clients.d.ts +3 -3
  143. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  144. package/dest/test-helpers/mock-pubsub.d.ts +27 -1
  145. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  146. package/dest/test-helpers/mock-pubsub.js +97 -2
  147. package/dest/test-helpers/reqresp-nodes.d.ts +1 -1
  148. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  149. package/dest/test-helpers/reqresp-nodes.js +2 -1
  150. package/dest/test-helpers/testbench-utils.d.ts +40 -38
  151. package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
  152. package/dest/test-helpers/testbench-utils.js +128 -59
  153. package/dest/testbench/p2p_client_testbench_worker.js +2 -2
  154. package/package.json +14 -14
  155. package/src/client/factory.ts +81 -13
  156. package/src/client/interface.ts +45 -14
  157. package/src/client/p2p_client.ts +151 -161
  158. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +1 -1
  159. package/src/config.ts +34 -2
  160. package/src/index.ts +1 -0
  161. package/src/mem_pools/attestation_pool/attestation_pool.ts +496 -91
  162. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +442 -102
  163. package/src/mem_pools/attestation_pool/index.ts +9 -2
  164. package/src/mem_pools/index.ts +4 -1
  165. package/src/mem_pools/interface.ts +4 -4
  166. package/src/mem_pools/tx_pool_v2/README.md +75 -9
  167. package/src/mem_pools/tx_pool_v2/deleted_pool.ts +310 -0
  168. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +3 -0
  169. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.ts +1 -1
  170. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +1 -1
  171. package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +3 -1
  172. package/src/mem_pools/tx_pool_v2/index.ts +1 -0
  173. package/src/mem_pools/tx_pool_v2/interfaces.ts +8 -2
  174. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +55 -5
  175. package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +18 -2
  176. package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +7 -1
  177. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +122 -80
  178. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +2 -2
  179. package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +2 -2
  180. package/src/msg_validators/tx_validator/block_header_validator.ts +15 -3
  181. package/src/msg_validators/tx_validator/double_spend_validator.ts +11 -6
  182. package/src/msg_validators/tx_validator/timestamp_validator.ts +19 -14
  183. package/src/services/dummy_service.ts +12 -0
  184. package/src/services/encoding.ts +2 -2
  185. package/src/services/gossipsub/README.md +626 -0
  186. package/src/services/gossipsub/index.ts +2 -0
  187. package/src/services/gossipsub/scoring.ts +29 -5
  188. package/src/services/gossipsub/topic_score_params.ts +451 -0
  189. package/src/services/libp2p/libp2p_service.ts +360 -269
  190. package/src/services/peer-manager/peer_scoring.ts +25 -0
  191. package/src/services/reqresp/interface.ts +26 -1
  192. package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +4 -3
  193. package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +17 -0
  194. package/src/services/reqresp/protocols/tx.ts +22 -0
  195. package/src/services/reqresp/reqresp.ts +13 -3
  196. package/src/services/service.ts +40 -0
  197. package/src/services/tx_collection/config.ts +68 -0
  198. package/src/services/tx_collection/fast_tx_collection.ts +14 -2
  199. package/src/services/tx_collection/file_store_tx_collection.ts +198 -0
  200. package/src/services/tx_collection/file_store_tx_source.ts +73 -0
  201. package/src/services/tx_collection/index.ts +1 -0
  202. package/src/services/tx_collection/slow_tx_collection.ts +64 -30
  203. package/src/services/tx_collection/tx_collection.ts +109 -13
  204. package/src/services/tx_collection/tx_collection_sink.ts +17 -7
  205. package/src/services/tx_file_store/config.ts +0 -6
  206. package/src/services/tx_file_store/tx_file_store.ts +9 -7
  207. package/src/services/tx_provider.ts +8 -7
  208. package/src/test-helpers/make-test-p2p-clients.ts +3 -3
  209. package/src/test-helpers/mock-pubsub.ts +133 -3
  210. package/src/test-helpers/reqresp-nodes.ts +2 -1
  211. package/src/test-helpers/testbench-utils.ts +127 -71
  212. package/src/testbench/p2p_client_testbench_worker.ts +2 -2
  213. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +0 -40
  214. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +0 -1
  215. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +0 -218
  216. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +0 -31
  217. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +0 -1
  218. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +0 -180
  219. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +0 -320
  220. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +0 -264
@@ -72,7 +72,7 @@ export class TxPoolIndices {
72
72
  * Iterates pending transaction hashes in priority order.
73
73
  * @param order - 'desc' for highest priority first, 'asc' for lowest priority first
74
74
  */
75
- *iteratePendingByPriority(order: 'asc' | 'desc'): Generator<string> {
75
+ *iteratePendingByPriority(order: 'asc' | 'desc', filter?: (hash: string) => boolean): Generator<string> {
76
76
  // Use compareFee from tx_metadata, swap args for descending order
77
77
  const feeCompareFn = order === 'desc' ? (a: bigint, b: bigint) => compareFee(b, a) : compareFee;
78
78
  const hashCompareFn = order === 'desc' ? (a: string, b: string) => compareTxHash(b, a) : compareTxHash;
@@ -84,11 +84,27 @@ export class TxPoolIndices {
84
84
  // Use compareTxHash from tx_metadata, swap args for descending order
85
85
  const sortedHashes = [...hashesAtFee].sort(hashCompareFn);
86
86
  for (const hash of sortedHashes) {
87
- yield hash;
87
+ if (filter === undefined || filter(hash)) {
88
+ yield hash;
89
+ }
88
90
  }
89
91
  }
90
92
  }
91
93
 
94
+ /**
95
+ * Iterates pending transaction hashes in priority order, skipping txs received after maxReceivedAt.
96
+ * @param order - 'desc' for highest priority first, 'asc' for lowest priority first
97
+ * @param maxReceivedAt - Only yield txs with receivedAt <= this value
98
+ */
99
+ *iterateEligiblePendingByPriority(order: 'asc' | 'desc', maxReceivedAt: number): Generator<string> {
100
+ const filter = (hash: string) => {
101
+ const meta = this.#metadata.get(hash);
102
+ return meta !== undefined && meta.receivedAt <= maxReceivedAt;
103
+ };
104
+
105
+ yield* this.iteratePendingByPriority(order, filter);
106
+ }
107
+
92
108
  /** Iterates all metadata entries */
93
109
  *iterateMetadata(): Generator<[string, TxMetaData]> {
94
110
  yield* this.#metadata;
@@ -1,6 +1,7 @@
1
1
  import { SlotNumber } from '@aztec/foundation/branded-types';
2
2
  import { type Logger, createLogger } from '@aztec/foundation/log';
3
3
  import { SerialQueue } from '@aztec/foundation/queue';
4
+ import { DateProvider } from '@aztec/foundation/timer';
4
5
  import type { TypedEventEmitter } from '@aztec/foundation/types';
5
6
  import type { AztecAsyncKVStore } from '@aztec/kv-store';
6
7
  import type { L2Block, L2BlockId } from '@aztec/stdlib/block';
@@ -35,6 +36,7 @@ export class AztecKVTxPoolV2 extends (EventEmitter as new () => TypedEventEmitte
35
36
  deps: TxPoolV2Dependencies,
36
37
  telemetry: TelemetryClient = getTelemetryClient(),
37
38
  config: Partial<TxPoolV2Config> = {},
39
+ dateProvider: DateProvider = new DateProvider(),
38
40
  log = createLogger('p2p:tx_pool_v2'),
39
41
  ) {
40
42
  super();
@@ -59,7 +61,7 @@ export class AztecKVTxPoolV2 extends (EventEmitter as new () => TypedEventEmitte
59
61
  };
60
62
 
61
63
  // Create the implementation
62
- this.#impl = new TxPoolV2Impl(store, archiveStore, deps, callbacks, config, log);
64
+ this.#impl = new TxPoolV2Impl(store, archiveStore, deps, callbacks, config, dateProvider, log);
63
65
  }
64
66
 
65
67
  // ============================================================================
@@ -132,6 +134,10 @@ export class AztecKVTxPoolV2 extends (EventEmitter as new () => TypedEventEmitte
132
134
  return this.#queue.put(() => Promise.resolve(this.#impl.getPendingTxHashes()));
133
135
  }
134
136
 
137
+ getEligiblePendingTxHashes(): Promise<TxHash[]> {
138
+ return this.#queue.put(() => Promise.resolve(this.#impl.getEligiblePendingTxHashes()));
139
+ }
140
+
135
141
  getPendingTxCount(): Promise<number> {
136
142
  return this.#queue.put(() => Promise.resolve(this.#impl.getPendingTxCount()));
137
143
  }
@@ -1,5 +1,6 @@
1
- import { SlotNumber } from '@aztec/foundation/branded-types';
1
+ import { BlockNumber, SlotNumber } from '@aztec/foundation/branded-types';
2
2
  import type { Logger } from '@aztec/foundation/log';
3
+ import type { DateProvider } from '@aztec/foundation/timer';
3
4
  import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
4
5
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
5
6
  import { computeFeePayerBalanceStorageSlot } from '@aztec/protocol-contracts/fee-juice';
@@ -10,6 +11,7 @@ import { DatabasePublicStateSource } from '@aztec/stdlib/trees';
10
11
  import { BlockHeader, Tx, TxHash, type TxValidator } from '@aztec/stdlib/tx';
11
12
 
12
13
  import { TxArchive } from './archive/index.js';
14
+ import { DeletedPool } from './deleted_pool.js';
13
15
  import {
14
16
  EvictionManager,
15
17
  FeePayerBalanceEvictionRule,
@@ -53,7 +55,7 @@ export class TxPoolV2Impl {
53
55
  // === Dependencies ===
54
56
  #l2BlockSource: L2BlockSource;
55
57
  #worldStateSynchronizer: WorldStateSynchronizer;
56
- #pendingTxValidator: TxValidator<Tx>;
58
+ #createTxValidator: TxPoolV2Dependencies['createTxValidator'];
57
59
 
58
60
  // === In-Memory Indices ===
59
61
  #indices: TxPoolIndices = new TxPoolIndices();
@@ -61,7 +63,9 @@ export class TxPoolV2Impl {
61
63
  // === Config & Services ===
62
64
  #config: TxPoolV2Config;
63
65
  #archive: TxArchive;
66
+ #deletedPool: DeletedPool;
64
67
  #evictionManager: EvictionManager;
68
+ #dateProvider: DateProvider;
65
69
  #log: Logger;
66
70
  #callbacks: TxPoolV2Callbacks;
67
71
 
@@ -71,6 +75,7 @@ export class TxPoolV2Impl {
71
75
  deps: TxPoolV2Dependencies,
72
76
  callbacks: TxPoolV2Callbacks,
73
77
  config: Partial<TxPoolV2Config> = {},
78
+ dateProvider: DateProvider,
74
79
  log: Logger,
75
80
  ) {
76
81
  this.#store = store;
@@ -78,10 +83,12 @@ export class TxPoolV2Impl {
78
83
 
79
84
  this.#l2BlockSource = deps.l2BlockSource;
80
85
  this.#worldStateSynchronizer = deps.worldStateSynchronizer;
81
- this.#pendingTxValidator = deps.pendingTxValidator;
86
+ this.#createTxValidator = deps.createTxValidator;
82
87
 
83
88
  this.#config = { ...DEFAULT_TX_POOL_V2_CONFIG, ...config };
84
89
  this.#archive = new TxArchive(archiveStore, this.#config.archivedTxLimit, log);
90
+ this.#deletedPool = new DeletedPool(store, this.#txsDB, log);
91
+ this.#dateProvider = dateProvider;
85
92
  this.#log = log;
86
93
  this.#callbacks = callbacks;
87
94
 
@@ -116,7 +123,10 @@ export class TxPoolV2Impl {
116
123
  * by running pre-add rules to resolve nullifier conflicts, balance checks, and pool size limits.
117
124
  */
118
125
  async hydrateFromDatabase(): Promise<void> {
119
- // Step 1: Load all transactions from DB
126
+ // Step 0: Hydrate deleted pool state
127
+ await this.#deletedPool.hydrateFromDatabase();
128
+
129
+ // Step 1: Load all transactions from DB (excluding soft-deleted)
120
130
  const { loaded, errors: deserializationErrors } = await this.#loadAllTxsFromDb();
121
131
 
122
132
  // Step 2: Check mined status for each tx
@@ -134,7 +144,10 @@ export class TxPoolV2Impl {
134
144
  }
135
145
 
136
146
  // Step 4: Validate non-mined transactions
137
- const { valid, invalid } = await this.#validateTxBatch(nonMined, 'on startup');
147
+ const { valid, invalid } = await this.#revalidateMetadata(
148
+ nonMined.map(e => e.meta),
149
+ 'on startup',
150
+ );
138
151
 
139
152
  // Step 5: Populate mined indices (these don't need conflict resolution)
140
153
  for (const meta of mined) {
@@ -155,7 +168,7 @@ export class TxPoolV2Impl {
155
168
  await this.#txsDB.delete(txHashStr);
156
169
  }
157
170
  });
158
- this.#log.info(`Deleted ${toDelete.length} invalid/rejected transactions on startup`);
171
+ this.#log.info(`Deleted ${toDelete.length} invalid/rejected transactions on startup`, { txHashes: toDelete });
159
172
  }
160
173
 
161
174
  async addPendingTxs(txs: Tx[], opts: { source?: string }): Promise<AddTxsResult> {
@@ -229,13 +242,13 @@ export class TxPoolV2Impl {
229
242
  const txHash = tx.getTxHash();
230
243
  const txHashStr = txHash.toString();
231
244
 
232
- // Validate transaction
233
- if (!(await this.#validateTx(tx))) {
245
+ // Build metadata and validate using metadata
246
+ const meta = await buildTxMetaData(tx);
247
+ if (!(await this.#validateMeta(meta))) {
234
248
  return { status: 'rejected' };
235
249
  }
236
250
 
237
- // Build metadata and run pre-add rules
238
- const meta = await buildTxMetaData(tx);
251
+ // Run pre-add rules
239
252
  const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
240
253
 
241
254
  if (preAddResult.shouldIgnore) {
@@ -246,7 +259,10 @@ export class TxPoolV2Impl {
246
259
  // Evict conflicts
247
260
  for (const evictHashStr of preAddResult.txHashesToEvict) {
248
261
  await this.#deleteTx(evictHashStr);
249
- this.#log.debug(`Evicted tx ${evictHashStr} due to higher-fee tx ${txHashStr}`);
262
+ this.#log.debug(`Evicted tx ${evictHashStr} due to higher-fee tx ${txHashStr}`, {
263
+ evictedTxHash: evictHashStr,
264
+ replacementTxHash: txHashStr,
265
+ });
250
266
  if (acceptedPending.has(evictHashStr)) {
251
267
  // Evicted tx was from this batch - mark as ignored in result
252
268
  acceptedPending.delete(evictHashStr);
@@ -267,14 +283,14 @@ export class TxPoolV2Impl {
267
283
  return 'ignored';
268
284
  }
269
285
 
270
- // Validate transaction (no logging for dry-run check)
271
- const validationResult = await this.#pendingTxValidator.validateTx(tx);
272
- if (validationResult.result !== 'valid') {
286
+ // Build metadata and validate using metadata
287
+ const meta = await buildTxMetaData(tx);
288
+ const validationResult = await this.#validateMeta(meta, undefined, 'can add pending');
289
+ if (validationResult !== true) {
273
290
  return 'rejected';
274
291
  }
275
292
 
276
- // Build metadata and use pre-add rules
277
- const meta = await buildTxMetaData(tx);
293
+ // Use pre-add rules
278
294
  const poolAccess = this.#createPreAddPoolAccess();
279
295
  const preAddResult = await this.#evictionManager.runPreAddRules(meta, poolAccess);
280
296
 
@@ -347,6 +363,7 @@ export class TxPoolV2Impl {
347
363
  // Add new mined tx (callback emitted by #addTx)
348
364
  await this.#addTx(tx, { mined: blockId }, opts);
349
365
  }
366
+ await this.#deletedPool.clearIfMinedHigher(txHashStr, blockId.number);
350
367
  }
351
368
  });
352
369
  }
@@ -373,6 +390,7 @@ export class TxPoolV2Impl {
373
390
  // Step 4: Mark txs as mined (only those we have in the pool)
374
391
  for (const meta of found) {
375
392
  this.#indices.markAsMined(meta, blockId);
393
+ await this.#deletedPool.clearIfMinedHigher(meta.txHash, blockId.number);
376
394
  }
377
395
 
378
396
  // Step 5: Run eviction rules (remove pending txs with conflicting nullifiers/expired timestamps)
@@ -382,6 +400,9 @@ export class TxPoolV2Impl {
382
400
  }
383
401
 
384
402
  async prepareForSlot(slotNumber: SlotNumber): Promise<void> {
403
+ // Step 0: Clean up slot-deleted txs from previous slots
404
+ await this.#deletedPool.cleanupSlotDeleted(slotNumber);
405
+
385
406
  // Step 1: Find expired protected txs
386
407
  const expiredProtected = this.#indices.findExpiredProtectedTxs(slotNumber);
387
408
 
@@ -397,7 +418,7 @@ export class TxPoolV2Impl {
397
418
  this.#log.info(`Preparing for slot ${slotNumber}: unprotecting ${txsToRestore.length} txs`);
398
419
 
399
420
  // Step 4: Validate for pending pool
400
- const { valid, invalid } = await this.#loadAndValidateTxs(txsToRestore, 'during prepareForSlot');
421
+ const { valid, invalid } = await this.#revalidateMetadata(txsToRestore, 'during prepareForSlot');
401
422
 
402
423
  // Step 5: Resolve nullifier conflicts and add winners to pending indices
403
424
  const { added, toEvict } = this.#applyNullifierConflictResolution(valid);
@@ -426,24 +447,39 @@ export class TxPoolV2Impl {
426
447
 
427
448
  this.#log.info(`Handling prune to block ${latestBlock.number}: un-mining ${txsToUnmine.length} txs`);
428
449
 
429
- // Step 2: Unmine - clear mined status from metadata
450
+ // Step 2: Mark ALL un-mined txs with their original mined block number
451
+ // This ensures they get soft-deleted if removed later, and only hard-deleted
452
+ // when their original mined block is finalized
453
+ await this.#deletedPool.markFromPrunedBlock(
454
+ txsToUnmine.map(m => ({
455
+ txHash: m.txHash,
456
+ minedAtBlock: BlockNumber(m.minedL2BlockId!.number),
457
+ })),
458
+ );
459
+
460
+ // Step 3: Unmine - clear mined status from metadata
430
461
  for (const meta of txsToUnmine) {
431
462
  this.#indices.markAsUnmined(meta);
432
463
  }
433
464
 
434
- // Step 3: Filter out protected txs (they'll be handled by prepareForSlot)
465
+ // Step 4: Filter out protected txs (they'll be handled by prepareForSlot)
435
466
  const unprotectedTxs = this.#indices.filterUnprotected(txsToUnmine);
436
467
 
437
468
  // Step 4: Validate for pending pool
438
- const { valid, invalid } = await this.#loadAndValidateTxs(unprotectedTxs, 'during handlePrunedBlocks');
469
+ const { valid, invalid } = await this.#revalidateMetadata(unprotectedTxs, 'during handlePrunedBlocks');
439
470
 
440
- // Step 5: Resolve nullifier conflicts and add winners to pending indices
471
+ // Step 6: Resolve nullifier conflicts and add winners to pending indices
441
472
  const { toEvict } = this.#applyNullifierConflictResolution(valid);
442
473
 
443
- // Step 6: Delete invalid and evicted txs
474
+ // Step 7: Delete invalid and evicted txs
444
475
  await this.#deleteTxsBatch([...invalid, ...toEvict]);
445
476
 
446
- // Step 7: Run eviction rules for ALL pending txs (not just restored ones)
477
+ this.#log.info(
478
+ `Handled prune to block ${latestBlock.number}: ${valid.length} txs restored to pending, ${invalid.length} invalid, ${toEvict.length} evicted due to nullifier conflicts`,
479
+ { txHashesRestored: valid.map(m => m.txHash), txHashesInvalid: invalid, txHashesEvicted: toEvict },
480
+ );
481
+
482
+ // Step 8: Run eviction rules for ALL pending txs (not just restored ones)
447
483
  // This handles cases like existing pending txs with invalid fee payer balances
448
484
  await this.#evictionManager.evictAfterChainPrune(latestBlock.number);
449
485
  }
@@ -452,22 +488,19 @@ export class TxPoolV2Impl {
452
488
  // Delete failed txs
453
489
  await this.#deleteTxsBatch(txHashes.map(h => h.toString()));
454
490
 
455
- this.#log.info(`Deleted ${txHashes.length} failed txs`);
491
+ this.#log.info(`Deleted ${txHashes.length} failed txs`, { txHashes: txHashes.map(h => h.toString()) });
456
492
  }
457
493
 
458
494
  async handleFinalizedBlock(block: BlockHeader): Promise<void> {
459
495
  const blockNumber = block.globalVariables.blockNumber;
460
496
 
461
- // Step 1: Find txs mined at or before finalized block
462
- const txsToFinalize = this.#indices.findTxsMinedAtOrBefore(blockNumber);
463
- if (txsToFinalize.length === 0) {
464
- return;
465
- }
497
+ // Step 1: Find mined txs at or before finalized block
498
+ const minedTxsToFinalize = this.#indices.findTxsMinedAtOrBefore(blockNumber);
466
499
 
467
- // Step 2: Collect txs for archiving (before deletion)
500
+ // Step 2: Collect mined txs for archiving (before deletion)
468
501
  const txsToArchive: Tx[] = [];
469
502
  if (this.#archive.isEnabled()) {
470
- for (const txHashStr of txsToFinalize) {
503
+ for (const txHashStr of minedTxsToFinalize) {
471
504
  const buffer = await this.#txsDB.getAsync(txHashStr);
472
505
  if (buffer) {
473
506
  txsToArchive.push(Tx.fromBuffer(buffer));
@@ -475,15 +508,22 @@ export class TxPoolV2Impl {
475
508
  }
476
509
  }
477
510
 
478
- // Step 3: Delete from active pool
479
- await this.#deleteTxsBatch(txsToFinalize);
511
+ // Step 3: Delete mined txs from active pool
512
+ await this.#deleteTxsBatch(minedTxsToFinalize);
480
513
 
481
- // Step 4: Archive
514
+ // Step 4: Finalize soft-deleted txs
515
+ await this.#deletedPool.finalizeBlock(blockNumber);
516
+
517
+ // Step 5: Archive mined txs
482
518
  if (txsToArchive.length > 0) {
483
519
  await this.#archive.archiveTxs(txsToArchive);
484
520
  }
485
521
 
486
- this.#log.info(`Finalized ${txsToFinalize.length} txs from blocks up to ${blockNumber}`);
522
+ if (minedTxsToFinalize.length > 0) {
523
+ this.#log.info(`Finalized ${minedTxsToFinalize.length} mined txs from blocks up to ${blockNumber}`, {
524
+ txHashes: minedTxsToFinalize,
525
+ });
526
+ }
487
527
  }
488
528
 
489
529
  // === Query Methods ===
@@ -503,21 +543,36 @@ export class TxPoolV2Impl {
503
543
  }
504
544
 
505
545
  hasTxs(txHashes: TxHash[]): boolean[] {
506
- return txHashes.map(h => this.#indices.has(h.toString()));
546
+ return txHashes.map(h => {
547
+ const hashStr = h.toString();
548
+ return this.#indices.has(hashStr) || this.#deletedPool.isSoftDeleted(hashStr);
549
+ });
507
550
  }
508
551
 
509
552
  getTxStatus(txHash: TxHash): TxState | undefined {
510
- const meta = this.#indices.getMetadata(txHash.toString());
511
- if (!meta) {
512
- return undefined;
553
+ const txHashStr = txHash.toString();
554
+ const meta = this.#indices.getMetadata(txHashStr);
555
+ if (meta) {
556
+ return this.#indices.getTxState(meta);
557
+ }
558
+ // Check if soft-deleted
559
+ if (this.#deletedPool.isSoftDeleted(txHashStr)) {
560
+ return 'deleted';
513
561
  }
514
- return this.#indices.getTxState(meta);
562
+ return undefined;
515
563
  }
516
564
 
517
565
  getPendingTxHashes(): TxHash[] {
518
566
  return [...this.#indices.iteratePendingByPriority('desc')].map(hash => TxHash.fromString(hash));
519
567
  }
520
568
 
569
+ getEligiblePendingTxHashes(): TxHash[] {
570
+ const maxReceivedAt = this.#dateProvider.now() - this.#config.minTxPoolAgeMs;
571
+ return [...this.#indices.iterateEligiblePendingByPriority('desc', maxReceivedAt)].map(hash =>
572
+ TxHash.fromString(hash),
573
+ );
574
+ }
575
+
521
576
  getPendingTxCount(): number {
522
577
  return this.#indices.getPendingTxCount();
523
578
  }
@@ -562,6 +617,9 @@ export class TxPoolV2Impl {
562
617
  this.#config.archivedTxLimit = config.archivedTxLimit;
563
618
  this.#archive.updateLimit(config.archivedTxLimit);
564
619
  }
620
+ if (config.minTxPoolAgeMs !== undefined) {
621
+ this.#config.minTxPoolAgeMs = config.minTxPoolAgeMs;
622
+ }
565
623
  // Update eviction rules with new config
566
624
  this.#evictionManager.updateConfig(config);
567
625
  }
@@ -598,8 +656,10 @@ export class TxPoolV2Impl {
598
656
  ): Promise<TxMetaData> {
599
657
  const txHashStr = tx.getTxHash().toString();
600
658
  const meta = await buildTxMetaData(tx);
659
+ meta.receivedAt = this.#dateProvider.now();
601
660
 
602
661
  await this.#txsDB.set(txHashStr, tx.toBuffer());
662
+ await this.#deletedPool.clearSoftDeleted(txHashStr);
603
663
  this.#callbacks.onTxsAdded([tx], opts);
604
664
 
605
665
  if (state === 'pending') {
@@ -624,10 +684,15 @@ export class TxPoolV2Impl {
624
684
  * Deletes a transaction from both indices and DB.
625
685
  * Emits onTxsRemoved callback immediately after DB delete.
626
686
  */
687
+ /**
688
+ * Deletes a transaction from the pool.
689
+ * Delegates to DeletedPool which decides soft vs hard delete based on whether
690
+ * the tx is from a pruned block.
691
+ */
627
692
  async #deleteTx(txHashStr: string): Promise<void> {
628
693
  this.#indices.remove(txHashStr);
629
- await this.#txsDB.delete(txHashStr);
630
694
  this.#callbacks.onTxsRemoved([txHashStr]);
695
+ await this.#deletedPool.deleteTx(txHashStr);
631
696
  }
632
697
 
633
698
  /** Deletes a batch of transactions, emitting callbacks individually for each. */
@@ -641,64 +706,36 @@ export class TxPoolV2Impl {
641
706
  // PRIVATE HELPERS - Validation & Conflict Resolution
642
707
  // ============================================================================
643
708
 
644
- /** Validates a single transaction, returning true if valid */
645
- async #validateTx(tx: Tx, context?: string): Promise<boolean> {
646
- const result = await this.#pendingTxValidator.validateTx(tx);
709
+ /** Validates transaction metadata, returning true if valid */
710
+ async #validateMeta(meta: TxMetaData, validator?: TxValidator<TxMetaData>, context?: string): Promise<boolean> {
711
+ const txValidator = validator ?? (await this.#createTxValidator());
712
+ const result = await txValidator.validateTx(meta);
647
713
  if (result.result !== 'valid') {
648
714
  const contextStr = context ? ` ${context}` : '';
649
- this.#log.info(`Tx ${tx.getTxHash()}${contextStr} failed validation: ${result.reason?.join(', ')}`);
715
+ this.#log.info(`Tx ${meta.txHash}${contextStr} failed validation: ${result.reason?.join(', ')}`);
650
716
  return false;
651
717
  }
652
718
  return true;
653
719
  }
654
720
 
655
- /** Loads transactions from DB, returning loaded txs and missing hashes */
656
- async #loadTxsFromDb(metas: TxMetaData[]): Promise<{ loaded: { tx: Tx; meta: TxMetaData }[]; missing: string[] }> {
657
- const loaded: { tx: Tx; meta: TxMetaData }[] = [];
658
- const missing: string[] = [];
659
-
660
- for (const meta of metas) {
661
- const buffer = await this.#txsDB.getAsync(meta.txHash);
662
- if (!buffer) {
663
- this.#log.warn(`Tx ${meta.txHash} not found in DB`);
664
- missing.push(meta.txHash);
665
- continue;
666
- }
667
- loaded.push({ tx: Tx.fromBuffer(buffer), meta });
668
- }
669
-
670
- return { loaded, missing };
671
- }
672
-
673
- /** Validates a batch of transactions, returning valid and invalid groups */
674
- async #validateTxBatch(
675
- txs: { tx: Tx; meta: TxMetaData }[],
721
+ /** Validates metadata directly */
722
+ async #revalidateMetadata(
723
+ metas: TxMetaData[],
676
724
  context?: string,
677
725
  ): Promise<{ valid: TxMetaData[]; invalid: string[] }> {
678
726
  const valid: TxMetaData[] = [];
679
727
  const invalid: string[] = [];
680
-
681
- for (const { tx, meta } of txs) {
682
- if (await this.#validateTx(tx, context)) {
728
+ const validator = await this.#createTxValidator();
729
+ for (const meta of metas) {
730
+ if (await this.#validateMeta(meta, validator, context)) {
683
731
  valid.push(meta);
684
732
  } else {
685
733
  invalid.push(meta.txHash);
686
734
  }
687
735
  }
688
-
689
736
  return { valid, invalid };
690
737
  }
691
738
 
692
- /** Loads transactions from DB and validates them */
693
- async #loadAndValidateTxs(
694
- metas: TxMetaData[],
695
- context?: string,
696
- ): Promise<{ valid: TxMetaData[]; invalid: string[] }> {
697
- const { loaded, missing } = await this.#loadTxsFromDb(metas);
698
- const { valid, invalid } = await this.#validateTxBatch(loaded, context);
699
- return { valid, invalid: [...missing, ...invalid] };
700
- }
701
-
702
739
  /**
703
740
  * Resolves nullifier conflicts between incoming txs and existing pending txs.
704
741
  * Modifies the pending indices during iteration to maintain consistent state
@@ -768,6 +805,11 @@ export class TxPoolV2Impl {
768
805
  const errors: string[] = [];
769
806
 
770
807
  for await (const [txHashStr, buffer] of this.#txsDB.entriesAsync()) {
808
+ // Skip soft-deleted transactions - they stay in DB but not in indices
809
+ if (this.#deletedPool.isSoftDeleted(txHashStr)) {
810
+ continue;
811
+ }
812
+
771
813
  try {
772
814
  const tx = Tx.fromBuffer(buffer);
773
815
  const meta = await buildTxMetaData(tx);
@@ -2,7 +2,7 @@ import type { EpochCacheInterface } from '@aztec/epoch-cache';
2
2
  import { type CheckpointAttestation, PeerErrorSeverity, type ValidationResult } from '@aztec/stdlib/p2p';
3
3
  import { Attributes, Metrics, type TelemetryClient, createUpDownCounterWithDefault } from '@aztec/telemetry-client';
4
4
 
5
- import type { AttestationPool } from '../../mem_pools/attestation_pool/attestation_pool.js';
5
+ import type { AttestationPoolApi } from '../../mem_pools/attestation_pool/attestation_pool.js';
6
6
  import { CheckpointAttestationValidator } from './attestation_validator.js';
7
7
 
8
8
  /**
@@ -18,7 +18,7 @@ export class FishermanAttestationValidator extends CheckpointAttestationValidato
18
18
 
19
19
  constructor(
20
20
  epochCache: EpochCacheInterface,
21
- private attestationPool: AttestationPool,
21
+ private attestationPool: AttestationPoolApi,
22
22
  telemetryClient: TelemetryClient,
23
23
  ) {
24
24
  super(epochCache);
@@ -1,6 +1,6 @@
1
- import type { ProcessedTx, Tx, TxValidationResult, TxValidator } from '@aztec/stdlib/tx';
1
+ import type { TxValidationResult, TxValidator } from '@aztec/stdlib/tx';
2
2
 
3
- export class AggregateTxValidator<T extends Tx | ProcessedTx> implements TxValidator<T> {
3
+ export class AggregateTxValidator<T> implements TxValidator<T> {
4
4
  #validators: TxValidator<T>[];
5
5
  constructor(...validators: TxValidator<T>[]) {
6
6
  if (validators.length === 0) {
@@ -1,12 +1,24 @@
1
1
  import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
2
2
  import type { BlockHash } from '@aztec/stdlib/block';
3
- import { type AnyTx, TX_ERROR_BLOCK_HEADER, type TxValidationResult, type TxValidator } from '@aztec/stdlib/tx';
3
+ import { TX_ERROR_BLOCK_HEADER, type TxValidationResult, type TxValidator } from '@aztec/stdlib/tx';
4
4
 
5
5
  export interface ArchiveSource {
6
6
  getArchiveIndices: (archives: BlockHash[]) => Promise<(bigint | undefined)[]>;
7
7
  }
8
8
 
9
- export class BlockHeaderTxValidator<T extends AnyTx> implements TxValidator<T> {
9
+ /** Structural interface for block header validation. */
10
+ export interface HasBlockHeaderData {
11
+ txHash: { toString(): string };
12
+ data: {
13
+ constants: {
14
+ anchorBlockHeader: {
15
+ hash(): Promise<BlockHash>;
16
+ };
17
+ };
18
+ };
19
+ }
20
+
21
+ export class BlockHeaderTxValidator<T extends HasBlockHeaderData> implements TxValidator<T> {
10
22
  #log: Logger;
11
23
  #archiveSource: ArchiveSource;
12
24
 
@@ -18,7 +30,7 @@ export class BlockHeaderTxValidator<T extends AnyTx> implements TxValidator<T> {
18
30
  async validateTx(tx: T): Promise<TxValidationResult> {
19
31
  const [index] = await this.#archiveSource.getArchiveIndices([await tx.data.constants.anchorBlockHeader.hash()]);
20
32
  if (index === undefined) {
21
- this.#log.verbose(`Rejecting tx ${'txHash' in tx ? tx.txHash : tx.hash} for referencing an unknown block header`);
33
+ this.#log.verbose(`Rejecting tx ${tx.txHash} for referencing an unknown block header`);
22
34
  return { result: 'invalid', reason: [TX_ERROR_BLOCK_HEADER] };
23
35
  }
24
36
  return { result: 'valid' };
@@ -1,9 +1,8 @@
1
+ import type { Fr } from '@aztec/foundation/curves/bn254';
1
2
  import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
2
3
  import {
3
- type AnyTx,
4
4
  TX_ERROR_DUPLICATE_NULLIFIER_IN_TX,
5
5
  TX_ERROR_EXISTING_NULLIFIER,
6
- Tx,
7
6
  type TxValidationResult,
8
7
  type TxValidator,
9
8
  } from '@aztec/stdlib/tx';
@@ -12,7 +11,13 @@ export interface NullifierSource {
12
11
  nullifiersExist: (nullifiers: Buffer[]) => Promise<boolean[]>;
13
12
  }
14
13
 
15
- export class DoubleSpendTxValidator<T extends AnyTx> implements TxValidator<T> {
14
+ /** Structural interface for double-spend validation. */
15
+ export interface HasNullifierData {
16
+ txHash: { toString(): string };
17
+ data: { getNonEmptyNullifiers(): Fr[] };
18
+ }
19
+
20
+ export class DoubleSpendTxValidator<T extends HasNullifierData> implements TxValidator<T> {
16
21
  #log: Logger;
17
22
  #nullifierSource: NullifierSource;
18
23
 
@@ -22,17 +27,17 @@ export class DoubleSpendTxValidator<T extends AnyTx> implements TxValidator<T> {
22
27
  }
23
28
 
24
29
  async validateTx(tx: T): Promise<TxValidationResult> {
25
- const nullifiers = tx instanceof Tx ? tx.data.getNonEmptyNullifiers() : tx.txEffect.nullifiers;
30
+ const nullifiers = tx.data.getNonEmptyNullifiers();
26
31
 
27
32
  // Ditch this tx if it has repeated nullifiers
28
33
  const uniqueNullifiers = new Set(nullifiers.map(n => n.toBigInt()));
29
34
  if (uniqueNullifiers.size !== nullifiers.length) {
30
- this.#log.verbose(`Rejecting tx ${'txHash' in tx ? tx.txHash : tx.hash} for emitting duplicate nullifiers`);
35
+ this.#log.verbose(`Rejecting tx ${tx.txHash} for emitting duplicate nullifiers`);
31
36
  return { result: 'invalid', reason: [TX_ERROR_DUPLICATE_NULLIFIER_IN_TX] };
32
37
  }
33
38
 
34
39
  if ((await this.#nullifierSource.nullifiersExist(nullifiers.map(n => n.toBuffer()))).some(Boolean)) {
35
- this.#log.verbose(`Rejecting tx ${'txHash' in tx ? tx.txHash : tx.hash} for repeating a nullifier`);
40
+ this.#log.verbose(`Rejecting tx ${tx.txHash} for repeating a nullifier`);
36
41
  return { result: 'invalid', reason: [TX_ERROR_EXISTING_NULLIFIER] };
37
42
  }
38
43
 
@@ -1,15 +1,24 @@
1
- import { BlockNumber } from '@aztec/foundation/branded-types';
1
+ import type { BlockNumber } from '@aztec/foundation/branded-types';
2
2
  import { type Logger, type LoggerBindings, createLogger } from '@aztec/foundation/log';
3
- import {
4
- type AnyTx,
5
- TX_ERROR_INVALID_INCLUDE_BY_TIMESTAMP,
6
- type TxValidationResult,
7
- type TxValidator,
8
- getTxHash,
9
- } from '@aztec/stdlib/tx';
3
+ import { TX_ERROR_INVALID_INCLUDE_BY_TIMESTAMP, type TxValidationResult, type TxValidator } from '@aztec/stdlib/tx';
10
4
  import type { UInt64 } from '@aztec/stdlib/types';
11
5
 
12
- export class TimestampTxValidator<T extends AnyTx> implements TxValidator<T> {
6
+ /** Structural interface for timestamp validation. */
7
+ export interface HasTimestampData {
8
+ txHash: { toString(): string };
9
+ data: {
10
+ includeByTimestamp: bigint;
11
+ constants: {
12
+ anchorBlockHeader: {
13
+ globalVariables: {
14
+ blockNumber: BlockNumber;
15
+ };
16
+ };
17
+ };
18
+ };
19
+ }
20
+
21
+ export class TimestampTxValidator<T extends HasTimestampData> implements TxValidator<T> {
13
22
  #log: Logger;
14
23
 
15
24
  constructor(
@@ -38,11 +47,7 @@ export class TimestampTxValidator<T extends AnyTx> implements TxValidator<T> {
38
47
  );
39
48
  }
40
49
  this.#log.verbose(
41
- `Rejecting tx ${getTxHash(
42
- tx,
43
- )} for low expiration timestamp. Tx expiration timestamp: ${includeByTimestamp}, timestamp: ${
44
- this.values.timestamp
45
- }.`,
50
+ `Rejecting tx ${tx.txHash} for low expiration timestamp. Tx expiration timestamp: ${includeByTimestamp}, timestamp: ${this.values.timestamp}.`,
46
51
  );
47
52
  return Promise.resolve({ result: 'invalid', reason: [TX_ERROR_INVALID_INCLUDE_BY_TIMESTAMP] });
48
53
  } else {