@aztec/p2p 3.0.0-rc.5 → 4.0.0-nightly.20260107

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 (98) hide show
  1. package/dest/client/factory.d.ts +2 -2
  2. package/dest/client/factory.d.ts.map +1 -1
  3. package/dest/client/factory.js +2 -3
  4. package/dest/client/p2p_client.d.ts +2 -2
  5. package/dest/client/p2p_client.d.ts.map +1 -1
  6. package/dest/client/p2p_client.js +395 -21
  7. package/dest/config.d.ts +4 -7
  8. package/dest/config.d.ts.map +1 -1
  9. package/dest/config.js +6 -9
  10. package/dest/mem_pools/instrumentation.d.ts +7 -1
  11. package/dest/mem_pools/instrumentation.d.ts.map +1 -1
  12. package/dest/mem_pools/instrumentation.js +29 -2
  13. package/dest/mem_pools/interface.d.ts +3 -4
  14. package/dest/mem_pools/interface.d.ts.map +1 -1
  15. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +28 -24
  16. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
  17. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +261 -323
  18. package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts +18 -0
  19. package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts.map +1 -0
  20. package/dest/mem_pools/tx_pool/eviction/eviction_manager.js +56 -0
  21. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts +83 -0
  22. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts.map +1 -0
  23. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.js +5 -0
  24. package/dest/mem_pools/tx_pool/eviction/insufficient_fee_payer_balance_rule.d.ts +15 -0
  25. package/dest/mem_pools/tx_pool/eviction/insufficient_fee_payer_balance_rule.d.ts.map +1 -0
  26. package/dest/mem_pools/tx_pool/eviction/insufficient_fee_payer_balance_rule.js +88 -0
  27. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts +17 -0
  28. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts.map +1 -0
  29. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.js +84 -0
  30. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts +19 -0
  31. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts.map +1 -0
  32. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.js +76 -0
  33. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts +26 -0
  34. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts.map +1 -0
  35. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.js +84 -0
  36. package/dest/mem_pools/tx_pool/index.d.ts +1 -2
  37. package/dest/mem_pools/tx_pool/index.d.ts.map +1 -1
  38. package/dest/mem_pools/tx_pool/index.js +0 -1
  39. package/dest/mem_pools/tx_pool/priority.d.ts +5 -1
  40. package/dest/mem_pools/tx_pool/priority.d.ts.map +1 -1
  41. package/dest/mem_pools/tx_pool/priority.js +6 -1
  42. package/dest/mem_pools/tx_pool/tx_pool.d.ts +8 -4
  43. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +1 -1
  44. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +1 -1
  45. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +1 -1
  46. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +25 -20
  47. package/dest/services/libp2p/libp2p_service.d.ts +4 -4
  48. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  49. package/dest/services/libp2p/libp2p_service.js +447 -64
  50. package/dest/services/peer-manager/metrics.d.ts +6 -1
  51. package/dest/services/peer-manager/metrics.d.ts.map +1 -1
  52. package/dest/services/peer-manager/metrics.js +17 -0
  53. package/dest/services/peer-manager/peer_manager.d.ts +1 -1
  54. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  55. package/dest/services/peer-manager/peer_manager.js +385 -9
  56. package/dest/services/reqresp/protocols/tx.d.ts +2 -3
  57. package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
  58. package/dest/services/reqresp/reqresp.js +402 -24
  59. package/dest/services/tx_provider.d.ts +2 -1
  60. package/dest/services/tx_provider.d.ts.map +1 -1
  61. package/dest/services/tx_provider.js +11 -2
  62. package/dest/services/tx_provider_instrumentation.d.ts +5 -2
  63. package/dest/services/tx_provider_instrumentation.d.ts.map +1 -1
  64. package/dest/services/tx_provider_instrumentation.js +14 -1
  65. package/dest/test-helpers/reqresp-nodes.d.ts +2 -2
  66. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
  67. package/dest/testbench/p2p_client_testbench_worker.js +1 -0
  68. package/package.json +14 -14
  69. package/src/client/factory.ts +5 -10
  70. package/src/client/p2p_client.ts +12 -17
  71. package/src/config.ts +8 -14
  72. package/src/mem_pools/instrumentation.ts +33 -0
  73. package/src/mem_pools/interface.ts +2 -4
  74. package/src/mem_pools/tx_pool/README.md +255 -0
  75. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +308 -368
  76. package/src/mem_pools/tx_pool/eviction/eviction_manager.ts +71 -0
  77. package/src/mem_pools/tx_pool/eviction/eviction_strategy.ts +93 -0
  78. package/src/mem_pools/tx_pool/eviction/insufficient_fee_payer_balance_rule.ts +108 -0
  79. package/src/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.ts +104 -0
  80. package/src/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.ts +91 -0
  81. package/src/mem_pools/tx_pool/eviction/low_priority_eviction_rule.ts +106 -0
  82. package/src/mem_pools/tx_pool/index.ts +0 -1
  83. package/src/mem_pools/tx_pool/priority.ts +8 -1
  84. package/src/mem_pools/tx_pool/tx_pool.ts +8 -3
  85. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +18 -13
  86. package/src/services/libp2p/libp2p_service.ts +12 -17
  87. package/src/services/peer-manager/metrics.ts +22 -0
  88. package/src/services/peer-manager/peer_manager.ts +2 -0
  89. package/src/services/reqresp/protocols/tx.ts +1 -2
  90. package/src/services/tx_provider.ts +17 -2
  91. package/src/services/tx_provider_instrumentation.ts +19 -2
  92. package/src/test-helpers/mock-pubsub.ts +1 -1
  93. package/src/test-helpers/reqresp-nodes.ts +1 -1
  94. package/src/testbench/p2p_client_testbench_worker.ts +2 -1
  95. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts +0 -81
  96. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +0 -1
  97. package/dest/mem_pools/tx_pool/memory_tx_pool.js +0 -239
  98. package/src/mem_pools/tx_pool/memory_tx_pool.ts +0 -285
@@ -0,0 +1,71 @@
1
+ import { Fr } from '@aztec/foundation/curves/bn254';
2
+ import { createLogger } from '@aztec/foundation/log';
3
+ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
4
+ import { BlockHeader, TxHash } from '@aztec/stdlib/tx';
5
+
6
+ import type { TxPoolOptions } from '../tx_pool.js';
7
+ import { type EvictionContext, EvictionEvent, type EvictionRule, type TxPoolOperations } from './eviction_strategy.js';
8
+
9
+ export class EvictionManager {
10
+ private rules: EvictionRule[] = [];
11
+
12
+ constructor(
13
+ private txPool: TxPoolOperations,
14
+ private log = createLogger('p2p:mempool:tx_pool:eviction_manager'),
15
+ ) {}
16
+
17
+ public async evictAfterNewTxs(newTxs: TxHash[]): Promise<void> {
18
+ const ctx: EvictionContext = {
19
+ event: EvictionEvent.TXS_ADDED,
20
+ newTxs,
21
+ };
22
+ await this.runEvictionRules(ctx);
23
+ }
24
+
25
+ public async evictAfterNewBlock(
26
+ block: BlockHeader,
27
+ newNullifiers: Fr[],
28
+ minedFeePayers: AztecAddress[],
29
+ ): Promise<void> {
30
+ const ctx: EvictionContext = {
31
+ event: EvictionEvent.BLOCK_MINED,
32
+ block,
33
+ newNullifiers,
34
+ minedFeePayers,
35
+ };
36
+
37
+ await this.runEvictionRules(ctx);
38
+ }
39
+
40
+ public async evictAfterChainPrune(blockNumber: number): Promise<void> {
41
+ const ctx: EvictionContext = {
42
+ event: EvictionEvent.CHAIN_PRUNED,
43
+ blockNumber,
44
+ };
45
+ await this.runEvictionRules(ctx);
46
+ }
47
+
48
+ public registerRule(rule: EvictionRule) {
49
+ this.rules.push(rule);
50
+ }
51
+
52
+ public updateConfig(config: TxPoolOptions): void {
53
+ for (const rule of this.rules) {
54
+ rule.updateConfig(config);
55
+ }
56
+ }
57
+
58
+ private async runEvictionRules(ctx: EvictionContext): Promise<void> {
59
+ for (const rule of this.rules) {
60
+ try {
61
+ await rule.evict(ctx, this.txPool);
62
+ } catch (err) {
63
+ this.log.warn(`Eviction rule ${rule.name} unexpected error: ${String(err)}`, {
64
+ err,
65
+ evictionRule: rule.name,
66
+ evictionEvent: ctx.event,
67
+ });
68
+ }
69
+ }
70
+ }
71
+ }
@@ -0,0 +1,93 @@
1
+ import { Fr } from '@aztec/foundation/curves/bn254';
2
+ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
3
+ import type { BlockHeader, Tx, TxHash } from '@aztec/stdlib/tx';
4
+
5
+ import type { TxPoolOptions } from '../tx_pool.js';
6
+
7
+ export const EvictionEvent = {
8
+ TXS_ADDED: 'txs_added',
9
+ BLOCK_MINED: 'block_mined',
10
+ CHAIN_PRUNED: 'chain_pruned',
11
+ } as const;
12
+
13
+ type EvictionEvent = (typeof EvictionEvent)[keyof typeof EvictionEvent];
14
+
15
+ export type EvictionContext =
16
+ | {
17
+ event: typeof EvictionEvent.TXS_ADDED;
18
+ newTxs: TxHash[];
19
+ }
20
+ | {
21
+ event: typeof EvictionEvent.CHAIN_PRUNED;
22
+ blockNumber: number;
23
+ }
24
+ | {
25
+ event: typeof EvictionEvent.BLOCK_MINED;
26
+ block: BlockHeader;
27
+ newNullifiers: Fr[];
28
+ minedFeePayers: AztecAddress[];
29
+ };
30
+
31
+ /**
32
+ * Result of an eviction operation
33
+ */
34
+ export interface EvictionResult {
35
+ readonly txsEvicted: TxHash[];
36
+ readonly reason: string;
37
+ readonly success: boolean;
38
+ readonly error?: Error;
39
+ }
40
+
41
+ /**
42
+ * Information about a pending transaction
43
+ */
44
+ export interface PendingTxInfo {
45
+ txHash: TxHash;
46
+ blockHash: Fr;
47
+ isEvictable: boolean;
48
+ }
49
+
50
+ /**
51
+ * Information about a transaction that references a specific block
52
+ */
53
+ export interface TxBlockReference {
54
+ txHash: TxHash;
55
+ blockHash: Fr;
56
+ isEvictable: boolean;
57
+ }
58
+
59
+ /**
60
+ * Operations that eviction strategies can perform on the pool
61
+ */
62
+ export interface TxPoolOperations {
63
+ getTxByHash(txHash: TxHash): Promise<Tx | undefined>;
64
+ getPendingTxInfos(): Promise<PendingTxInfo[]>;
65
+ getPendingTxsReferencingBlocks(blockHashes: Fr[]): Promise<TxBlockReference[]>;
66
+ getPendingTxsWithFeePayer(feePayer: AztecAddress[]): Promise<PendingTxInfo[]>;
67
+ /** Cheap count of current pending transactions. */
68
+ getPendingTxCount(): Promise<number>;
69
+ /**
70
+ * Returns up to `limit` lowest-priority evictable pending tx hashes.
71
+ * Ordering should be from lowest priority upwards.
72
+ */
73
+ getLowestPriorityEvictable(limit: number): Promise<TxHash[]>;
74
+ deleteTxs(txHashes: TxHash[], opts?: { permanently?: boolean }): Promise<void>;
75
+ }
76
+
77
+ /**
78
+ * Strategy interface for different eviction behaviors
79
+ */
80
+ export interface EvictionRule {
81
+ readonly name: string;
82
+
83
+ /**
84
+ * Performs the eviction logic
85
+ */
86
+ evict(context: EvictionContext, txPool: TxPoolOperations): Promise<EvictionResult>;
87
+
88
+ /**
89
+ * Updates the configuration for this eviction rule.
90
+ * Rules should ignore config options that don't apply to them.
91
+ */
92
+ updateConfig(config: TxPoolOptions): void;
93
+ }
@@ -0,0 +1,108 @@
1
+ import { createLogger } from '@aztec/foundation/log';
2
+ import { ProtocolContractAddress } from '@aztec/protocol-contracts';
3
+ import { GasFees } from '@aztec/stdlib/gas';
4
+ import type { ReadonlyWorldStateAccess } from '@aztec/stdlib/interfaces/server';
5
+ import { DatabasePublicStateSource, type MerkleTreeReadOperations } from '@aztec/stdlib/trees';
6
+ import type { TxHash } from '@aztec/stdlib/tx';
7
+
8
+ import { GasTxValidator } from '../../../msg_validators/index.js';
9
+ import type { TxPoolOptions } from '../tx_pool.js';
10
+ import {
11
+ type EvictionContext,
12
+ EvictionEvent,
13
+ type EvictionResult,
14
+ type EvictionRule,
15
+ type PendingTxInfo,
16
+ type TxPoolOperations,
17
+ } from './eviction_strategy.js';
18
+
19
+ export class InsufficientFeePayerBalanceRule implements EvictionRule {
20
+ public readonly name = 'InsufficientFeePayerBalance';
21
+ public readonly reason = 'insufficient_fee_juice';
22
+
23
+ public constructor(private worldState: ReadonlyWorldStateAccess) {}
24
+
25
+ private log = createLogger('p2p:mempool:tx_pool:insufficient_fee_payer_balance_rule');
26
+
27
+ async evict(context: EvictionContext, txPool: TxPoolOperations): Promise<EvictionResult> {
28
+ try {
29
+ if (context.event === EvictionEvent.CHAIN_PRUNED) {
30
+ const affectedTxs = await txPool.getPendingTxInfos();
31
+ return this.evictTxs(affectedTxs, context.blockNumber, txPool);
32
+ }
33
+
34
+ if (context.event === EvictionEvent.BLOCK_MINED) {
35
+ const affectedTxs = await txPool.getPendingTxsWithFeePayer(context.minedFeePayers);
36
+
37
+ // TODO: fix this edge-case
38
+ // This can lead to a race condition if we are catching up in the p2p client.
39
+ // Let's say we have 3 txs for the same fee payer, which get mined in blocks 1, 2, 3.
40
+ // Tx1 consumes fee juice, tx2 increases it, tx3 consumes it again. We see block1 with tx1 first, run this rule, and evict tx3.
41
+ // But tx3 was valid (due to tx2) and mined on block3. And we have just removed from the mempool a tx we needed for proving/reexec.
42
+ // -----
43
+ // Proposed fix: evict only if node is synched
44
+ return this.evictTxs(affectedTxs, context.block.getBlockNumber(), txPool);
45
+ }
46
+
47
+ return {
48
+ reason: this.reason,
49
+ success: true,
50
+ txsEvicted: [],
51
+ };
52
+ } catch (err) {
53
+ this.log.error('Failed to evict invalid transactions after mining', { err });
54
+ return {
55
+ reason: this.reason,
56
+ success: false,
57
+ txsEvicted: [],
58
+ error: new Error('Failed to evict invalid txs after mining', { cause: err }),
59
+ };
60
+ }
61
+ }
62
+
63
+ private async evictTxs(
64
+ candidateTxs: PendingTxInfo[],
65
+ blockNumber: number,
66
+ txPool: TxPoolOperations,
67
+ ): Promise<EvictionResult> {
68
+ const txsToEvict: TxHash[] = [];
69
+ const gasValidator = this.createGasTxValidator(this.worldState.getSnapshot(blockNumber));
70
+
71
+ for (const { txHash, isEvictable } of candidateTxs) {
72
+ if (!isEvictable) {
73
+ continue;
74
+ }
75
+
76
+ const tx = await txPool.getTxByHash(txHash);
77
+ if (!tx) {
78
+ continue;
79
+ }
80
+
81
+ this.log.debug(`Validating tx balance ${txHash}`);
82
+
83
+ if ((await gasValidator.validateTxFee(tx)).result === 'invalid') {
84
+ this.log.verbose(`Evicting tx ${txHash} from pool due to an insufficient fee payer balance`);
85
+ txsToEvict.push(txHash);
86
+ continue;
87
+ }
88
+ }
89
+
90
+ if (txsToEvict.length > 0) {
91
+ await txPool.deleteTxs(txsToEvict);
92
+ }
93
+
94
+ this.log.verbose(`Evicted ${txsToEvict.length} invalid txs after block mined`, { txsToEvict });
95
+
96
+ return {
97
+ reason: this.reason,
98
+ success: true,
99
+ txsEvicted: txsToEvict,
100
+ };
101
+ }
102
+
103
+ private createGasTxValidator(db: MerkleTreeReadOperations): GasTxValidator {
104
+ return new GasTxValidator(new DatabasePublicStateSource(db), ProtocolContractAddress.FeeJuice, GasFees.empty());
105
+ }
106
+
107
+ updateConfig(_config: TxPoolOptions): void {}
108
+ }
@@ -0,0 +1,104 @@
1
+ import { createLogger } from '@aztec/foundation/log';
2
+ import type { TxHash } from '@aztec/stdlib/tx';
3
+
4
+ import type { TxPoolOptions } from '../tx_pool.js';
5
+ import {
6
+ type EvictionContext,
7
+ EvictionEvent,
8
+ type EvictionResult,
9
+ type EvictionRule,
10
+ type TxPoolOperations,
11
+ } from './eviction_strategy.js';
12
+
13
+ /**
14
+ * Eviction rule that removes invalid transactions after a block is mined.
15
+ * Only triggers on BLOCK_MINED events.
16
+ *
17
+ * Eviction criteria includes:
18
+ * - Transactions with nullifiers that are already included in the mined block
19
+ * - Transactions with an expiration timestamp less than or equal to the mined block timestamp
20
+ */
21
+ export class InvalidTxsAfterMiningRule implements EvictionRule {
22
+ public readonly name = 'InvalidTxsAfterMining';
23
+
24
+ private log = createLogger('p2p:mempool:tx_pool:invalid_txs_after_mining_rule');
25
+
26
+ async evict(context: EvictionContext, txPool: TxPoolOperations): Promise<EvictionResult> {
27
+ if (context.event !== EvictionEvent.BLOCK_MINED) {
28
+ return {
29
+ reason: 'block_mined_invalid_txs',
30
+ success: true,
31
+ txsEvicted: [],
32
+ };
33
+ }
34
+
35
+ if (!context.block || !context.newNullifiers) {
36
+ this.log.warn('Invalid context for block mined eviction', { context });
37
+ return {
38
+ reason: 'block_mined_invalid_txs',
39
+ success: false,
40
+ txsEvicted: [],
41
+ error: new Error('Invalid block mined context'),
42
+ };
43
+ }
44
+
45
+ try {
46
+ const { timestamp } = context.block.globalVariables;
47
+
48
+ const txsToEvict: TxHash[] = [];
49
+ const pendingTxs = await txPool.getPendingTxInfos();
50
+ const minedNullifiers = new Set(context.newNullifiers.map(n => n.toString()));
51
+
52
+ for (const { txHash, isEvictable } of pendingTxs) {
53
+ if (!isEvictable) {
54
+ continue;
55
+ }
56
+
57
+ const tx = await txPool.getTxByHash(txHash);
58
+ if (!tx) {
59
+ continue;
60
+ }
61
+
62
+ // Evict pending txs that share nullifiers with mined txs
63
+ const txNullifiers = tx.data.getNonEmptyNullifiers();
64
+ if (txNullifiers.some(nullifier => minedNullifiers.has(nullifier.toString()))) {
65
+ this.log.verbose(`Evicting tx ${txHash} from pool due to a duplicate nullifier with a mined tx`);
66
+ txsToEvict.push(txHash);
67
+ continue;
68
+ }
69
+
70
+ // Evict pending txs with an expiration timestamp less than or equal to the mined block timestamp
71
+ const includeByTimestamp = tx.data.includeByTimestamp;
72
+ if (includeByTimestamp <= timestamp) {
73
+ this.log.verbose(
74
+ `Evicting tx ${txHash} from pool due to the tx being expired (includeByTimestamp: ${includeByTimestamp}, mined block timestamp: ${timestamp})`,
75
+ );
76
+ txsToEvict.push(txHash);
77
+ continue;
78
+ }
79
+ }
80
+
81
+ if (txsToEvict.length > 0) {
82
+ await txPool.deleteTxs(txsToEvict);
83
+ }
84
+
85
+ this.log.debug(`Evicted ${txsToEvict.length} invalid txs after block mined`);
86
+
87
+ return {
88
+ reason: 'block_mined_invalid_txs',
89
+ success: true,
90
+ txsEvicted: txsToEvict,
91
+ };
92
+ } catch (err) {
93
+ this.log.error('Failed to evict invalid transactions after mining', { err });
94
+ return {
95
+ reason: 'block_mined_invalid_txs',
96
+ success: false,
97
+ txsEvicted: [],
98
+ error: new Error('Failed to evict invalid txs after mining', { cause: err }),
99
+ };
100
+ }
101
+ }
102
+
103
+ updateConfig(_config: TxPoolOptions): void {}
104
+ }
@@ -0,0 +1,91 @@
1
+ import { findIndexInSortedArray, insertIntoSortedArray } from '@aztec/foundation/array';
2
+ import { Fr } from '@aztec/foundation/curves/bn254';
3
+ import { createLogger } from '@aztec/foundation/log';
4
+ import type { ReadonlyWorldStateAccess } from '@aztec/stdlib/interfaces/server';
5
+ import { MerkleTreeId } from '@aztec/stdlib/trees';
6
+ import type { TxHash } from '@aztec/stdlib/tx';
7
+
8
+ import type { TxPoolOptions } from '../tx_pool.js';
9
+ import {
10
+ type EvictionContext,
11
+ EvictionEvent,
12
+ type EvictionResult,
13
+ type EvictionRule,
14
+ type TxPoolOperations,
15
+ } from './eviction_strategy.js';
16
+
17
+ /**
18
+ * Eviction rule that removes invalid transactions after a blockchain reorganization.
19
+ * Only triggers on CHAIN_PRUNED events.
20
+ *
21
+ * Eviction criteria includes:
22
+ * - Transactions that reference pruned block hashes (invalid by definition)
23
+ */
24
+ export class InvalidTxsAfterReorgRule implements EvictionRule {
25
+ public readonly name = 'InvalidTxsAfterReorg';
26
+
27
+ private log = createLogger('p2p:mempool:tx_pool:invalid_txs_after_reorg_rule');
28
+
29
+ public constructor(private worldState: ReadonlyWorldStateAccess) {}
30
+
31
+ async evict(context: EvictionContext, txPool: TxPoolOperations): Promise<EvictionResult> {
32
+ if (context.event !== EvictionEvent.CHAIN_PRUNED) {
33
+ return {
34
+ reason: 'reorg_invalid_txs',
35
+ success: true,
36
+ txsEvicted: [],
37
+ };
38
+ }
39
+
40
+ try {
41
+ const candidateTxs = (await txPool.getPendingTxInfos()).filter(({ isEvictable }) => isEvictable);
42
+
43
+ // Deduplicate block hashes to reduce redundant DB lookups (many txs may share the same blockHash).
44
+ const uniqueBlockHashes: Fr[] = [];
45
+ candidateTxs.forEach(({ blockHash }) => {
46
+ insertIntoSortedArray(uniqueBlockHashes, blockHash, Fr.cmp, false);
47
+ });
48
+
49
+ const db = this.worldState.getSnapshot(context.blockNumber);
50
+ const blocksFromDb = await db.findLeafIndices(MerkleTreeId.ARCHIVE, uniqueBlockHashes);
51
+
52
+ // Identify txs whose blockHash is not found in the archive (pruned)
53
+ const txsToEvict: TxHash[] = [];
54
+ for (const tx of candidateTxs) {
55
+ const idx = findIndexInSortedArray(uniqueBlockHashes, tx.blockHash, Fr.cmp);
56
+ const blockPruned = idx === -1 || blocksFromDb[idx] === undefined;
57
+ if (blockPruned) {
58
+ txsToEvict.push(tx.txHash);
59
+ }
60
+ }
61
+
62
+ if (txsToEvict.length > 0) {
63
+ this.log.verbose(`Evicting ${txsToEvict.length} txs from pool due to referencing pruned blocks`);
64
+ await txPool.deleteTxs(txsToEvict);
65
+ }
66
+
67
+ const keptCount = candidateTxs.length - txsToEvict.length;
68
+ if (keptCount > 0) {
69
+ this.log.verbose(`Kept ${keptCount} txs that did not reference pruned blocks`);
70
+ }
71
+
72
+ this.log.debug(`Evicted ${txsToEvict.length} invalid txs after reorg`);
73
+
74
+ return {
75
+ reason: 'reorg_invalid_txs',
76
+ success: true,
77
+ txsEvicted: txsToEvict,
78
+ };
79
+ } catch (err) {
80
+ this.log.error('Failed to evict invalid transactions after reorg', { err });
81
+ return {
82
+ reason: 'reorg_invalid_txs',
83
+ success: false,
84
+ txsEvicted: [],
85
+ error: new Error('Failed to evict invalid txs after reorg', { cause: err }),
86
+ };
87
+ }
88
+ }
89
+
90
+ updateConfig(_config: TxPoolOptions): void {}
91
+ }
@@ -0,0 +1,106 @@
1
+ import { createLogger } from '@aztec/foundation/log';
2
+ import type { TxHash } from '@aztec/stdlib/tx';
3
+
4
+ import type { TxPoolOptions } from '../tx_pool.js';
5
+ import {
6
+ type EvictionContext,
7
+ EvictionEvent,
8
+ type EvictionResult,
9
+ type EvictionRule,
10
+ type TxPoolOperations,
11
+ } from './eviction_strategy.js';
12
+
13
+ export interface LowPriorityEvictionConfig {
14
+ /** Maximum number of pending transactions before eviction kicks in */
15
+ maxPoolSize: number;
16
+ }
17
+
18
+ /**
19
+ * Eviction rule that removes low-priority transactions when the number of pending transactions exceeds configured limits.
20
+ * Only triggers on TXS_ADDED events and respects non-evictable transactions.
21
+ */
22
+ export class LowPriorityEvictionRule implements EvictionRule {
23
+ public readonly name = 'LowPriorityEviction';
24
+
25
+ private log = createLogger('p2p:mempool:tx_pool:low_priority_eviction_rule');
26
+
27
+ constructor(private config: LowPriorityEvictionConfig) {}
28
+
29
+ public async evict(context: EvictionContext, txPool: TxPoolOperations): Promise<EvictionResult> {
30
+ if (context.event !== EvictionEvent.TXS_ADDED) {
31
+ return {
32
+ reason: 'low_priority',
33
+ success: true,
34
+ txsEvicted: [],
35
+ };
36
+ }
37
+
38
+ if (this.config.maxPoolSize === 0) {
39
+ return {
40
+ reason: 'low_priority',
41
+ success: true,
42
+ txsEvicted: [],
43
+ };
44
+ }
45
+
46
+ try {
47
+ const currentTxCount = await txPool.getPendingTxCount();
48
+ const maxCount = this.config.maxPoolSize;
49
+
50
+ if (currentTxCount <= maxCount) {
51
+ this.log.trace(`Not evicting low priority txs. Pending tx count below limit ${currentTxCount} <= ${maxCount}`);
52
+ return {
53
+ reason: 'low_priority',
54
+ success: true,
55
+ txsEvicted: [],
56
+ };
57
+ }
58
+
59
+ this.log.verbose(`Evicting low priority txs. Pending tx count above limit: ${currentTxCount} > ${maxCount}`);
60
+ const numberToEvict = currentTxCount - maxCount;
61
+ const txsToEvict: TxHash[] = await txPool.getLowestPriorityEvictable(numberToEvict);
62
+
63
+ if (txsToEvict.length > 0) {
64
+ await txPool.deleteTxs(txsToEvict);
65
+ }
66
+
67
+ const numNewTxsEvicted = context.newTxs.filter(newTxHash =>
68
+ txsToEvict.some(evictedTx => evictedTx.equals(newTxHash)),
69
+ ).length;
70
+
71
+ this.log.verbose(`Evicted ${txsToEvict.length} low priority txs, including ${numNewTxsEvicted} newly added txs`, {
72
+ txsEvicted: txsToEvict,
73
+ });
74
+
75
+ return {
76
+ reason: 'low_priority',
77
+ success: true,
78
+ txsEvicted: txsToEvict,
79
+ };
80
+ } catch (err) {
81
+ this.log.error('Failed to evict low priority transactions', { err });
82
+ return {
83
+ reason: 'low_priority',
84
+ success: false,
85
+ txsEvicted: [],
86
+ error: new Error('Failed to evict low priority txs', { cause: err }),
87
+ };
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Updates the configuration for this eviction rule
93
+ */
94
+ updateConfig(config: TxPoolOptions): void {
95
+ if (config.maxPendingTxCount !== undefined) {
96
+ this.config.maxPoolSize = config.maxPendingTxCount;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Gets the current configuration
102
+ */
103
+ getConfig(): LowPriorityEvictionConfig {
104
+ return { ...this.config };
105
+ }
106
+ }
@@ -1,3 +1,2 @@
1
1
  export * from './tx_pool.js';
2
- export * from './memory_tx_pool.js';
3
2
  export * from './aztec_kv_tx_pool.js';
@@ -7,7 +7,14 @@ import type { Tx } from '@aztec/stdlib/tx';
7
7
  * We currently use the sum of the priority fees for the tx for this value, represented as hex.
8
8
  */
9
9
  export function getPendingTxPriority(tx: Tx): string {
10
+ return Buffer32.fromBigInt(getTxPriorityFee(tx)).toString();
11
+ }
12
+
13
+ /**
14
+ * Returns the priority of a tx.
15
+ */
16
+ export function getTxPriorityFee(tx: Tx): bigint {
10
17
  const priorityFees = tx.getGasSettings().maxPriorityFeesPerGas;
11
18
  const totalFees = priorityFees.feePerDaGas + priorityFees.feePerL2Gas;
12
- return Buffer32.fromBigInt(totalFees).toString();
19
+ return totalFees;
13
20
  }
@@ -3,8 +3,7 @@ import type { TypedEventEmitter } from '@aztec/foundation/types';
3
3
  import type { BlockHeader, Tx, TxHash } from '@aztec/stdlib/tx';
4
4
 
5
5
  export type TxPoolOptions = {
6
- maxTxPoolSize?: number;
7
- txPoolOverflowFactor?: number;
6
+ maxPendingTxCount?: number;
8
7
  archivedTxLimit?: number;
9
8
  };
10
9
 
@@ -69,8 +68,9 @@ export interface TxPool extends TypedEventEmitter<TxPoolEvents> {
69
68
  * Moves mined txs back to the pending set in the case of a reorg.
70
69
  * Note: txs not known by this peer will be ignored.
71
70
  * @param txHashes - Hashes of the txs to flag as pending.
71
+ * @param latestBlock - The block number the chain was pruned to.
72
72
  */
73
- markMinedAsPending(txHashes: TxHash[]): Promise<void>;
73
+ markMinedAsPending(txHashes: TxHash[], latestBlock: BlockNumber): Promise<void>;
74
74
 
75
75
  /**
76
76
  * Deletes transactions from the pool. Tx hashes that are not present are ignored.
@@ -127,6 +127,11 @@ export interface TxPool extends TypedEventEmitter<TxPoolEvents> {
127
127
  */
128
128
  markTxsAsNonEvictable(txHashes: TxHash[]): Promise<void>;
129
129
 
130
+ /**
131
+ * Clears collection of non-evictable transactions in the pool
132
+ */
133
+ clearNonEvictableTxs(): Promise<void>;
134
+
130
135
  /**
131
136
  * Permanently deletes deleted mined transactions from blocks up to and including the specified block number.
132
137
  * @param blockNumber - Block number threshold. Deleted mined txs from this block or earlier will be permanently deleted.