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

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 (230) 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 +23 -5
  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 +11 -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 +39 -5
  50. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +105 -0
  51. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -0
  52. package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +345 -0
  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 +12 -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 +259 -520
  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 +368 -273
  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/batch-tx-requester/batch_tx_requester.d.ts +4 -4
  94. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
  95. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +8 -8
  96. package/dest/services/reqresp/interface.d.ts +10 -1
  97. package/dest/services/reqresp/interface.d.ts.map +1 -1
  98. package/dest/services/reqresp/interface.js +15 -1
  99. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +7 -5
  100. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
  101. package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +16 -11
  102. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +21 -10
  103. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
  104. package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +27 -11
  105. package/dest/services/reqresp/protocols/tx.d.ts +7 -1
  106. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
  107. package/dest/services/reqresp/protocols/tx.js +20 -0
  108. package/dest/services/reqresp/reqresp.d.ts +1 -1
  109. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  110. package/dest/services/reqresp/reqresp.js +11 -4
  111. package/dest/services/service.d.ts +35 -1
  112. package/dest/services/service.d.ts.map +1 -1
  113. package/dest/services/tx_collection/config.d.ts +22 -4
  114. package/dest/services/tx_collection/config.d.ts.map +1 -1
  115. package/dest/services/tx_collection/config.js +49 -3
  116. package/dest/services/tx_collection/fast_tx_collection.d.ts +6 -5
  117. package/dest/services/tx_collection/fast_tx_collection.d.ts.map +1 -1
  118. package/dest/services/tx_collection/fast_tx_collection.js +27 -17
  119. package/dest/services/tx_collection/file_store_tx_collection.d.ts +53 -0
  120. package/dest/services/tx_collection/file_store_tx_collection.d.ts.map +1 -0
  121. package/dest/services/tx_collection/file_store_tx_collection.js +165 -0
  122. package/dest/services/tx_collection/file_store_tx_source.d.ts +28 -0
  123. package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -0
  124. package/dest/services/tx_collection/file_store_tx_source.js +59 -0
  125. package/dest/services/tx_collection/index.d.ts +3 -2
  126. package/dest/services/tx_collection/index.d.ts.map +1 -1
  127. package/dest/services/tx_collection/index.js +1 -0
  128. package/dest/services/tx_collection/proposal_tx_collector.d.ts +12 -12
  129. package/dest/services/tx_collection/proposal_tx_collector.d.ts.map +1 -1
  130. package/dest/services/tx_collection/proposal_tx_collector.js +4 -5
  131. package/dest/services/tx_collection/slow_tx_collection.d.ts +6 -2
  132. package/dest/services/tx_collection/slow_tx_collection.d.ts.map +1 -1
  133. package/dest/services/tx_collection/slow_tx_collection.js +55 -23
  134. package/dest/services/tx_collection/tx_collection.d.ts +19 -7
  135. package/dest/services/tx_collection/tx_collection.d.ts.map +1 -1
  136. package/dest/services/tx_collection/tx_collection.js +75 -3
  137. package/dest/services/tx_collection/tx_collection_sink.d.ts +15 -6
  138. package/dest/services/tx_collection/tx_collection_sink.d.ts.map +1 -1
  139. package/dest/services/tx_collection/tx_collection_sink.js +13 -7
  140. package/dest/services/tx_file_store/config.d.ts +1 -3
  141. package/dest/services/tx_file_store/config.d.ts.map +1 -1
  142. package/dest/services/tx_file_store/config.js +0 -4
  143. package/dest/services/tx_file_store/tx_file_store.d.ts +4 -3
  144. package/dest/services/tx_file_store/tx_file_store.d.ts.map +1 -1
  145. package/dest/services/tx_file_store/tx_file_store.js +8 -5
  146. package/dest/services/tx_provider.d.ts +3 -3
  147. package/dest/services/tx_provider.d.ts.map +1 -1
  148. package/dest/services/tx_provider.js +5 -4
  149. package/dest/test-helpers/make-test-p2p-clients.d.ts +3 -3
  150. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -1
  151. package/dest/test-helpers/mock-pubsub.d.ts +27 -1
  152. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  153. package/dest/test-helpers/mock-pubsub.js +97 -2
  154. package/dest/test-helpers/reqresp-nodes.d.ts +1 -1
  155. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  156. package/dest/test-helpers/reqresp-nodes.js +2 -1
  157. package/dest/test-helpers/testbench-utils.d.ts +40 -38
  158. package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
  159. package/dest/test-helpers/testbench-utils.js +128 -59
  160. package/dest/testbench/p2p_client_testbench_worker.js +2 -2
  161. package/package.json +14 -14
  162. package/src/client/factory.ts +81 -13
  163. package/src/client/interface.ts +45 -14
  164. package/src/client/p2p_client.ts +151 -161
  165. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +1 -1
  166. package/src/config.ts +34 -2
  167. package/src/index.ts +1 -0
  168. package/src/mem_pools/attestation_pool/attestation_pool.ts +496 -91
  169. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +442 -102
  170. package/src/mem_pools/attestation_pool/index.ts +9 -2
  171. package/src/mem_pools/index.ts +4 -1
  172. package/src/mem_pools/interface.ts +4 -4
  173. package/src/mem_pools/tx_pool_v2/README.md +103 -16
  174. package/src/mem_pools/tx_pool_v2/deleted_pool.ts +310 -0
  175. package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +3 -0
  176. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.ts +1 -1
  177. package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +1 -1
  178. package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +3 -1
  179. package/src/mem_pools/tx_pool_v2/index.ts +1 -0
  180. package/src/mem_pools/tx_pool_v2/interfaces.ts +10 -2
  181. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +57 -6
  182. package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +433 -0
  183. package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +10 -1
  184. package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +266 -607
  185. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +2 -2
  186. package/src/msg_validators/tx_validator/aggregate_tx_validator.ts +2 -2
  187. package/src/msg_validators/tx_validator/block_header_validator.ts +15 -3
  188. package/src/msg_validators/tx_validator/double_spend_validator.ts +11 -6
  189. package/src/msg_validators/tx_validator/timestamp_validator.ts +19 -14
  190. package/src/services/dummy_service.ts +12 -0
  191. package/src/services/encoding.ts +2 -2
  192. package/src/services/gossipsub/README.md +626 -0
  193. package/src/services/gossipsub/index.ts +2 -0
  194. package/src/services/gossipsub/scoring.ts +29 -5
  195. package/src/services/gossipsub/topic_score_params.ts +451 -0
  196. package/src/services/libp2p/libp2p_service.ts +370 -275
  197. package/src/services/peer-manager/peer_scoring.ts +25 -0
  198. package/src/services/reqresp/batch-tx-requester/README.md +7 -7
  199. package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +11 -11
  200. package/src/services/reqresp/interface.ts +26 -1
  201. package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +23 -14
  202. package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +38 -15
  203. package/src/services/reqresp/protocols/tx.ts +22 -0
  204. package/src/services/reqresp/reqresp.ts +13 -3
  205. package/src/services/service.ts +40 -0
  206. package/src/services/tx_collection/config.ts +74 -6
  207. package/src/services/tx_collection/fast_tx_collection.ts +28 -26
  208. package/src/services/tx_collection/file_store_tx_collection.ts +198 -0
  209. package/src/services/tx_collection/file_store_tx_source.ts +73 -0
  210. package/src/services/tx_collection/index.ts +2 -1
  211. package/src/services/tx_collection/proposal_tx_collector.ts +12 -14
  212. package/src/services/tx_collection/slow_tx_collection.ts +64 -30
  213. package/src/services/tx_collection/tx_collection.ts +109 -13
  214. package/src/services/tx_collection/tx_collection_sink.ts +17 -7
  215. package/src/services/tx_file_store/config.ts +0 -6
  216. package/src/services/tx_file_store/tx_file_store.ts +9 -7
  217. package/src/services/tx_provider.ts +8 -7
  218. package/src/test-helpers/make-test-p2p-clients.ts +3 -3
  219. package/src/test-helpers/mock-pubsub.ts +133 -3
  220. package/src/test-helpers/reqresp-nodes.ts +2 -1
  221. package/src/test-helpers/testbench-utils.ts +127 -71
  222. package/src/testbench/p2p_client_testbench_worker.ts +2 -2
  223. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +0 -40
  224. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +0 -1
  225. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +0 -218
  226. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +0 -31
  227. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +0 -1
  228. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +0 -180
  229. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +0 -320
  230. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +0 -264
@@ -1,12 +1,27 @@
1
+ import { BlockNumber } from '@aztec/foundation/branded-types';
1
2
  import { Fr } from '@aztec/foundation/curves/bn254';
2
3
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
3
- import type { L2BlockId } from '@aztec/stdlib/block';
4
+ import { BlockHash, type L2BlockId } from '@aztec/stdlib/block';
4
5
  import type { Tx } from '@aztec/stdlib/tx';
5
6
 
6
7
  import { getFeePayerBalanceDelta } from '../../msg_validators/tx_validator/fee_payer_balance.js';
7
8
  import { getTxPriorityFee } from '../tx_pool/priority.js';
8
9
  import type { PreAddResult } from './eviction/interfaces.js';
9
10
 
11
+ /** Validator-compatible data interface, mirroring the subset of PrivateKernelTailCircuitPublicInputs used by validators. */
12
+ export type TxMetaValidationData = {
13
+ getNonEmptyNullifiers(): Fr[];
14
+ includeByTimestamp: bigint;
15
+ constants: {
16
+ anchorBlockHeader: {
17
+ hash(): Promise<BlockHash>;
18
+ globalVariables: {
19
+ blockNumber: BlockNumber;
20
+ };
21
+ };
22
+ };
23
+ };
24
+
10
25
  /**
11
26
  * Lightweight in-memory representation of a transaction.
12
27
  * Stored for every tx in the pool to enable efficient queries and challenges
@@ -42,22 +57,32 @@ export type TxMetaData = {
42
57
 
43
58
  /** Timestamp by which the transaction must be included (for expiration checks) */
44
59
  readonly includeByTimestamp: bigint;
60
+
61
+ /** Validator-compatible data, providing the same access patterns as Tx.data */
62
+ readonly data: TxMetaValidationData;
63
+
64
+ /** Timestamp (ms) when the tx was received into the pool. 0 for hydrated txs (always eligible). */
65
+ receivedAt: number;
45
66
  };
46
67
 
47
68
  /** Transaction state derived from TxMetaData fields and pool protection status */
48
- export type TxState = 'pending' | 'protected' | 'mined';
69
+ export type TxState = 'pending' | 'protected' | 'mined' | 'deleted';
49
70
 
50
71
  /**
51
72
  * Builds TxMetaData from a full Tx object.
52
73
  * Extracts all relevant fields for efficient in-memory storage and querying.
74
+ * Fr values are captured in closures for zero-cost re-validation.
53
75
  */
54
76
  export async function buildTxMetaData(tx: Tx): Promise<TxMetaData> {
55
77
  const txHash = tx.getTxHash().toString();
56
- const anchorBlockHeaderHash = (await tx.data.constants.anchorBlockHeader.hash()).toString();
78
+ const nullifierFrs = tx.data.getNonEmptyNullifiers();
79
+ const nullifiers = nullifierFrs.map(n => n.toString());
80
+ const anchorBlockHeaderHashFr = await tx.data.constants.anchorBlockHeader.hash();
81
+ const anchorBlockHeaderHash = anchorBlockHeaderHashFr.toString();
82
+ const includeByTimestamp = tx.data.includeByTimestamp;
83
+ const anchorBlockNumber = tx.data.constants.anchorBlockHeader.globalVariables.blockNumber;
57
84
  const priorityFee = getTxPriorityFee(tx);
58
85
  const feePayer = tx.data.feePayer.toString();
59
- const nullifiers = tx.data.getNonEmptyNullifiers().map(n => n.toString());
60
- const includeByTimestamp = tx.data.includeByTimestamp;
61
86
 
62
87
  const { feeLimit, claimAmount } = await getFeePayerBalanceDelta(tx, ProtocolContractAddress.FeeJuice);
63
88
 
@@ -70,6 +95,17 @@ export async function buildTxMetaData(tx: Tx): Promise<TxMetaData> {
70
95
  feeLimit,
71
96
  nullifiers,
72
97
  includeByTimestamp,
98
+ receivedAt: 0,
99
+ data: {
100
+ getNonEmptyNullifiers: () => nullifierFrs,
101
+ includeByTimestamp,
102
+ constants: {
103
+ anchorBlockHeader: {
104
+ hash: () => Promise.resolve(anchorBlockHeaderHashFr),
105
+ globalVariables: { blockNumber: anchorBlockNumber },
106
+ },
107
+ },
108
+ },
73
109
  };
74
110
  }
75
111
 
@@ -145,7 +181,8 @@ export function checkNullifierConflict(
145
181
 
146
182
  // If incoming tx has strictly higher priority, mark for eviction
147
183
  // Otherwise, ignore incoming tx (ties go to existing tx)
148
- if (incomingMeta.priorityFee > conflictingMeta.priorityFee) {
184
+ // Use comparePriority for deterministic ordering (includes txHash as tiebreaker)
185
+ if (comparePriority(incomingMeta, conflictingMeta) > 0) {
149
186
  txHashesToEvict.push(conflictingHashStr);
150
187
  } else {
151
188
  return {
@@ -158,3 +195,17 @@ export function checkNullifierConflict(
158
195
 
159
196
  return { shouldIgnore: false, txHashesToEvict };
160
197
  }
198
+
199
+ /** Creates a stub TxMetaValidationData for tests that don't exercise validators. */
200
+ export function stubTxMetaValidationData(overrides: { includeByTimestamp?: bigint } = {}): TxMetaValidationData {
201
+ return {
202
+ getNonEmptyNullifiers: () => [],
203
+ includeByTimestamp: overrides.includeByTimestamp ?? 0n,
204
+ constants: {
205
+ anchorBlockHeader: {
206
+ hash: () => Promise.resolve(new BlockHash(Fr.ZERO)),
207
+ globalVariables: { blockNumber: BlockNumber(0) },
208
+ },
209
+ },
210
+ };
211
+ }
@@ -0,0 +1,433 @@
1
+ import { SlotNumber } from '@aztec/foundation/branded-types';
2
+ import type { L2BlockId } from '@aztec/stdlib/block';
3
+
4
+ import { type TxMetaData, type TxState, compareFee, compareTxHash } from './tx_metadata.js';
5
+
6
+ /**
7
+ * Manages in-memory indices for the transaction pool.
8
+ *
9
+ * Tracks transaction metadata and maintains several indices for efficient querying:
10
+ * - Metadata by txHash (primary store)
11
+ * - Nullifier to txHash mapping (pending txs only)
12
+ * - Fee payer to txHashes mapping (pending txs only)
13
+ * - Priority-ordered pending txs
14
+ * - Protected transaction tracking
15
+ *
16
+ * Key invariant: Only pending txs appear in nullifier/feePayer/priority indices.
17
+ */
18
+ export class TxPoolIndices {
19
+ /** Primary metadata store: txHash -> TxMetaData */
20
+ #metadata: Map<string, TxMetaData> = new Map();
21
+ /** Nullifier to txHash index (pending txs only) */
22
+ #nullifierToTxHash: Map<string, string> = new Map();
23
+ /** Fee payer to txHashes index (pending txs only) */
24
+ #feePayerToTxHashes: Map<string, Set<string>> = new Map();
25
+ /** Pending txHashes grouped by priority fee */
26
+ #pendingByPriority: Map<bigint, Set<string>> = new Map();
27
+ /** Protected transactions: txHash -> slotNumber */
28
+ #protectedTransactions: Map<string, SlotNumber> = new Map();
29
+
30
+ // ============================================================================
31
+ // STATE QUERIES
32
+ // ============================================================================
33
+
34
+ /**
35
+ * Derives the transaction state from its metadata and protection status.
36
+ * A transaction is:
37
+ * - 'mined' if it has a minedL2BlockId
38
+ * - 'protected' if it's in the protectedTransactions map (but not mined)
39
+ * - 'pending' otherwise
40
+ */
41
+ getTxState(meta: TxMetaData): TxState {
42
+ if (meta.minedL2BlockId !== undefined) {
43
+ return 'mined';
44
+ } else if (this.#protectedTransactions.has(meta.txHash)) {
45
+ return 'protected';
46
+ } else {
47
+ return 'pending';
48
+ }
49
+ }
50
+
51
+ getMetadata(txHash: string): TxMetaData | undefined {
52
+ return this.#metadata.get(txHash);
53
+ }
54
+
55
+ has(txHash: string): boolean {
56
+ return this.#metadata.has(txHash);
57
+ }
58
+
59
+ isEmpty(): boolean {
60
+ return this.#metadata.size === 0;
61
+ }
62
+
63
+ getTxCount(): number {
64
+ return this.#metadata.size;
65
+ }
66
+
67
+ // ============================================================================
68
+ // ITERATION
69
+ // ============================================================================
70
+
71
+ /**
72
+ * Iterates pending transaction hashes in priority order.
73
+ * @param order - 'desc' for highest priority first, 'asc' for lowest priority first
74
+ */
75
+ *iteratePendingByPriority(order: 'asc' | 'desc', filter?: (hash: string) => boolean): Generator<string> {
76
+ // Use compareFee from tx_metadata, swap args for descending order
77
+ const feeCompareFn = order === 'desc' ? (a: bigint, b: bigint) => compareFee(b, a) : compareFee;
78
+ const hashCompareFn = order === 'desc' ? (a: string, b: string) => compareTxHash(b, a) : compareTxHash;
79
+
80
+ const sortedFees = [...this.#pendingByPriority.keys()].sort(feeCompareFn);
81
+
82
+ for (const fee of sortedFees) {
83
+ const hashesAtFee = this.#pendingByPriority.get(fee)!;
84
+ // Use compareTxHash from tx_metadata, swap args for descending order
85
+ const sortedHashes = [...hashesAtFee].sort(hashCompareFn);
86
+ for (const hash of sortedHashes) {
87
+ if (filter === undefined || filter(hash)) {
88
+ yield hash;
89
+ }
90
+ }
91
+ }
92
+ }
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
+
108
+ /** Iterates all metadata entries */
109
+ *iterateMetadata(): Generator<[string, TxMetaData]> {
110
+ yield* this.#metadata;
111
+ }
112
+
113
+ // ============================================================================
114
+ // INDEX MODIFICATIONS
115
+ // ============================================================================
116
+
117
+ /** Adds a new pending transaction to all indices */
118
+ addPending(meta: TxMetaData): void {
119
+ this.#metadata.set(meta.txHash, meta);
120
+ this.#addToPendingIndices(meta);
121
+ }
122
+
123
+ /** Adds a new protected transaction (not added to pending indices) */
124
+ addProtected(meta: TxMetaData, slot: SlotNumber): void {
125
+ this.#metadata.set(meta.txHash, meta);
126
+ this.#protectedTransactions.set(meta.txHash, slot);
127
+ }
128
+
129
+ /** Adds a new mined transaction (not added to pending indices) */
130
+ addMined(meta: TxMetaData): void {
131
+ this.#metadata.set(meta.txHash, meta);
132
+ }
133
+
134
+ /** Marks an existing transaction as mined and removes from pending indices */
135
+ markAsMined(meta: TxMetaData, blockId: L2BlockId): void {
136
+ meta.minedL2BlockId = blockId;
137
+ // Safe to call unconditionally - removeFromPendingIndices is idempotent
138
+ this.#removeFromPendingIndices(meta);
139
+ }
140
+
141
+ /** Clears the mined status from a transaction */
142
+ markAsUnmined(meta: TxMetaData): void {
143
+ meta.minedL2BlockId = undefined;
144
+ }
145
+
146
+ /**
147
+ * Updates protection status for an existing transaction.
148
+ * Removes from pending indices if transitioning from pending to protected.
149
+ */
150
+ updateProtection(txHash: string, slotNumber: SlotNumber): void {
151
+ const currentSlot = this.#protectedTransactions.get(txHash);
152
+
153
+ // Only update if not already protected at an equal or later slot
154
+ if (currentSlot !== undefined && currentSlot >= slotNumber) {
155
+ return;
156
+ }
157
+
158
+ // Remove from pending indices if transitioning from pending to protected
159
+ if (currentSlot === undefined) {
160
+ const meta = this.#metadata.get(txHash);
161
+ if (meta) {
162
+ this.#removeFromPendingIndices(meta);
163
+ }
164
+ }
165
+
166
+ this.#protectedTransactions.set(txHash, slotNumber);
167
+ }
168
+
169
+ /** Sets protection for a txHash that may not have metadata yet */
170
+ setProtection(txHash: string, slotNumber: SlotNumber): void {
171
+ this.#protectedTransactions.set(txHash, slotNumber);
172
+ }
173
+
174
+ /** Gets the protection slot for a txHash, if protected */
175
+ getProtectionSlot(txHash: string): SlotNumber | undefined {
176
+ return this.#protectedTransactions.get(txHash);
177
+ }
178
+
179
+ /** Removes protection from tx hashes */
180
+ clearProtection(txHashes: string[]): void {
181
+ for (const txHash of txHashes) {
182
+ this.#protectedTransactions.delete(txHash);
183
+ }
184
+ }
185
+
186
+ /** Removes a transaction from all indices */
187
+ remove(txHash: string): void {
188
+ const meta = this.#metadata.get(txHash);
189
+ if (!meta) {
190
+ return;
191
+ }
192
+
193
+ this.#metadata.delete(txHash);
194
+ this.#protectedTransactions.delete(txHash);
195
+ this.#removeFromPendingIndices(meta);
196
+ }
197
+
198
+ /** Removes a transaction from pending indices only (not metadata) */
199
+ removeFromPendingIndices(meta: TxMetaData): void {
200
+ this.#removeFromPendingIndices(meta);
201
+ }
202
+
203
+ /** Adds a transaction to pending indices (used during conflict resolution) */
204
+ addToPendingIndices(meta: TxMetaData): void {
205
+ this.#addToPendingIndices(meta);
206
+ }
207
+
208
+ // ============================================================================
209
+ // QUERIES FOR EVICTION RULES
210
+ // ============================================================================
211
+
212
+ /** Gets all pending transactions for a given fee payer */
213
+ getFeePayerPendingTxs(feePayer: string): TxMetaData[] {
214
+ const txHashes = this.#feePayerToTxHashes.get(feePayer);
215
+ if (!txHashes) {
216
+ return [];
217
+ }
218
+ const result: TxMetaData[] = [];
219
+ for (const txHash of txHashes) {
220
+ const meta = this.#metadata.get(txHash);
221
+ if (meta && this.getTxState(meta) === 'pending') {
222
+ result.push(meta);
223
+ }
224
+ }
225
+ return result;
226
+ }
227
+
228
+ /** Gets the count of pending transactions */
229
+ getPendingTxCount(): number {
230
+ let count = 0;
231
+ for (const hashes of this.#pendingByPriority.values()) {
232
+ count += hashes.size;
233
+ }
234
+ return count;
235
+ }
236
+
237
+ /** Gets the lowest priority pending transaction hashes (up to limit) */
238
+ getLowestPriorityPending(limit: number): string[] {
239
+ if (limit <= 0) {
240
+ return [];
241
+ }
242
+
243
+ const result: string[] = [];
244
+ for (const hash of this.iteratePendingByPriority('asc')) {
245
+ result.push(hash);
246
+ if (result.length >= limit) {
247
+ break;
248
+ }
249
+ }
250
+ return result;
251
+ }
252
+
253
+ /** Gets the lowest priority pending transaction */
254
+ getLowestPriorityPendingTx(): TxMetaData | undefined {
255
+ for (const txHash of this.iteratePendingByPriority('asc')) {
256
+ const meta = this.#metadata.get(txHash);
257
+ if (meta) {
258
+ return meta;
259
+ }
260
+ }
261
+ return undefined;
262
+ }
263
+
264
+ /** Gets all pending transactions */
265
+ getPendingTxs(): TxMetaData[] {
266
+ const result: TxMetaData[] = [];
267
+ for (const hashSet of this.#pendingByPriority.values()) {
268
+ for (const txHash of hashSet) {
269
+ const meta = this.#metadata.get(txHash);
270
+ if (meta) {
271
+ result.push(meta);
272
+ }
273
+ }
274
+ }
275
+ return result;
276
+ }
277
+
278
+ /** Gets all fee payers with pending transactions */
279
+ getPendingFeePayers(): string[] {
280
+ return Array.from(this.#feePayerToTxHashes.keys());
281
+ }
282
+
283
+ /** Gets the txHash that uses a given nullifier (pending txs only) */
284
+ getTxHashByNullifier(nullifier: string): string | undefined {
285
+ return this.#nullifierToTxHash.get(nullifier);
286
+ }
287
+
288
+ /** Gets txHashes for a fee payer */
289
+ getTxHashesByFeePayer(feePayer: string): Set<string> | undefined {
290
+ return this.#feePayerToTxHashes.get(feePayer);
291
+ }
292
+
293
+ // ============================================================================
294
+ // FIND/FILTER OPERATIONS
295
+ // ============================================================================
296
+
297
+ /** Finds all transactions mined in blocks after the given block number */
298
+ findTxsMinedAfter(blockNumber: number): TxMetaData[] {
299
+ const result: TxMetaData[] = [];
300
+ for (const meta of this.#metadata.values()) {
301
+ if (meta.minedL2BlockId !== undefined && meta.minedL2BlockId.number > blockNumber) {
302
+ result.push(meta);
303
+ }
304
+ }
305
+ return result;
306
+ }
307
+
308
+ /** Finds tx hashes mined at or before the given block number */
309
+ findTxsMinedAtOrBefore(blockNumber: number): string[] {
310
+ const result: string[] = [];
311
+ for (const [txHash, meta] of this.#metadata) {
312
+ if (meta.minedL2BlockId !== undefined && meta.minedL2BlockId.number <= blockNumber) {
313
+ result.push(txHash);
314
+ }
315
+ }
316
+ return result;
317
+ }
318
+
319
+ /** Finds protected tx hashes from slots earlier than the given slot number */
320
+ findExpiredProtectedTxs(slotNumber: SlotNumber): string[] {
321
+ const result: string[] = [];
322
+ for (const [txHash, protectedSlot] of this.#protectedTransactions) {
323
+ if (protectedSlot < slotNumber) {
324
+ result.push(txHash);
325
+ }
326
+ }
327
+ return result;
328
+ }
329
+
330
+ /** Filters out transactions that are currently protected */
331
+ filterUnprotected(txs: TxMetaData[]): TxMetaData[] {
332
+ return txs.filter(meta => !this.#protectedTransactions.has(meta.txHash));
333
+ }
334
+
335
+ /** Filters to transactions that have metadata and are not mined */
336
+ filterRestorable(txHashes: string[]): TxMetaData[] {
337
+ const result: TxMetaData[] = [];
338
+ for (const txHash of txHashes) {
339
+ const meta = this.#metadata.get(txHash);
340
+ if (meta && meta.minedL2BlockId === undefined) {
341
+ result.push(meta);
342
+ }
343
+ }
344
+ return result;
345
+ }
346
+
347
+ // ============================================================================
348
+ // METRICS
349
+ // ============================================================================
350
+
351
+ /** Counts transactions by state */
352
+ countTxs(): { pending: number; protected: number; mined: number } {
353
+ let pending = 0;
354
+ let protected_ = 0;
355
+ let mined = 0;
356
+
357
+ for (const meta of this.#metadata.values()) {
358
+ const state = this.getTxState(meta);
359
+ if (state === 'pending') {
360
+ pending++;
361
+ } else if (state === 'protected') {
362
+ protected_++;
363
+ } else if (state === 'mined') {
364
+ mined++;
365
+ }
366
+ }
367
+
368
+ return { pending, protected: protected_, mined };
369
+ }
370
+
371
+ /** Gets all mined transactions with their block IDs */
372
+ getMinedTxs(): [string, L2BlockId][] {
373
+ const result: [string, L2BlockId][] = [];
374
+ for (const [txHash, meta] of this.#metadata) {
375
+ if (meta.minedL2BlockId !== undefined) {
376
+ result.push([txHash, meta.minedL2BlockId]);
377
+ }
378
+ }
379
+ return result;
380
+ }
381
+
382
+ // ============================================================================
383
+ // PRIVATE HELPERS
384
+ // ============================================================================
385
+
386
+ #addToPendingIndices(meta: TxMetaData): void {
387
+ // Add to nullifier index
388
+ for (const nullifier of meta.nullifiers) {
389
+ this.#nullifierToTxHash.set(nullifier, meta.txHash);
390
+ }
391
+
392
+ // Add to fee payer index
393
+ let feePayerSet = this.#feePayerToTxHashes.get(meta.feePayer);
394
+ if (!feePayerSet) {
395
+ feePayerSet = new Set();
396
+ this.#feePayerToTxHashes.set(meta.feePayer, feePayerSet);
397
+ }
398
+ feePayerSet.add(meta.txHash);
399
+
400
+ // Add to priority bucket
401
+ let prioritySet = this.#pendingByPriority.get(meta.priorityFee);
402
+ if (!prioritySet) {
403
+ prioritySet = new Set();
404
+ this.#pendingByPriority.set(meta.priorityFee, prioritySet);
405
+ }
406
+ prioritySet.add(meta.txHash);
407
+ }
408
+
409
+ #removeFromPendingIndices(meta: TxMetaData): void {
410
+ // Remove from nullifier index
411
+ for (const nullifier of meta.nullifiers) {
412
+ this.#nullifierToTxHash.delete(nullifier);
413
+ }
414
+
415
+ // Remove from fee payer index
416
+ const feePayerSet = this.#feePayerToTxHashes.get(meta.feePayer);
417
+ if (feePayerSet) {
418
+ feePayerSet.delete(meta.txHash);
419
+ if (feePayerSet.size === 0) {
420
+ this.#feePayerToTxHashes.delete(meta.feePayer);
421
+ }
422
+ }
423
+
424
+ // Remove from priority map
425
+ const hashSet = this.#pendingByPriority.get(meta.priorityFee);
426
+ if (hashSet) {
427
+ hashSet.delete(meta.txHash);
428
+ if (hashSet.size === 0) {
429
+ this.#pendingByPriority.delete(meta.priorityFee);
430
+ }
431
+ }
432
+ }
433
+ }
@@ -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();
@@ -52,11 +54,14 @@ export class AztecKVTxPoolV2 extends (EventEmitter as new () => TypedEventEmitte
52
54
  },
53
55
  onTxsRemoved: (txHashes: string[] | bigint[]) => {
54
56
  this.#metrics?.transactionsRemoved(txHashes);
57
+ // Convert to TxHash objects for the event
58
+ const hashes = txHashes.map(h => (typeof h === 'string' ? TxHash.fromString(h) : TxHash.fromBigInt(h)));
59
+ this.emit('txs-removed', { txHashes: hashes });
55
60
  },
56
61
  };
57
62
 
58
63
  // Create the implementation
59
- this.#impl = new TxPoolV2Impl(store, archiveStore, deps, callbacks, config, log);
64
+ this.#impl = new TxPoolV2Impl(store, archiveStore, deps, callbacks, config, dateProvider, log);
60
65
  }
61
66
 
62
67
  // ============================================================================
@@ -129,6 +134,10 @@ export class AztecKVTxPoolV2 extends (EventEmitter as new () => TypedEventEmitte
129
134
  return this.#queue.put(() => Promise.resolve(this.#impl.getPendingTxHashes()));
130
135
  }
131
136
 
137
+ getEligiblePendingTxHashes(): Promise<TxHash[]> {
138
+ return this.#queue.put(() => Promise.resolve(this.#impl.getEligiblePendingTxHashes()));
139
+ }
140
+
132
141
  getPendingTxCount(): Promise<number> {
133
142
  return this.#queue.put(() => Promise.resolve(this.#impl.getPendingTxCount()));
134
143
  }