@aztec/p2p 0.0.1-commit.9ee6fcc6 → 0.0.1-commit.9ef841308

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 (117) hide show
  1. package/dest/client/factory.d.ts +1 -1
  2. package/dest/client/factory.d.ts.map +1 -1
  3. package/dest/client/factory.js +6 -5
  4. package/dest/client/test/tx_proposal_collector/proposal_tx_collector_worker.js +14 -3
  5. package/dest/index.d.ts +1 -2
  6. package/dest/index.d.ts.map +1 -1
  7. package/dest/index.js +0 -1
  8. package/dest/mem_pools/index.d.ts +1 -2
  9. package/dest/mem_pools/index.d.ts.map +1 -1
  10. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts +1 -1
  11. package/dest/mem_pools/tx_pool_v2/tx_metadata.d.ts.map +1 -1
  12. package/dest/mem_pools/tx_pool_v2/tx_metadata.js +5 -1
  13. package/dest/services/discv5/discV5_service.d.ts +1 -1
  14. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  15. package/dest/services/discv5/discV5_service.js +4 -2
  16. package/dest/services/libp2p/libp2p_service.d.ts +7 -2
  17. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  18. package/dest/services/libp2p/libp2p_service.js +115 -34
  19. package/dest/services/peer-manager/peer_manager.d.ts +6 -2
  20. package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
  21. package/dest/services/peer-manager/peer_manager.js +18 -6
  22. package/dest/services/peer-manager/peer_scoring.d.ts +5 -2
  23. package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
  24. package/dest/services/peer-manager/peer_scoring.js +28 -10
  25. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +1 -1
  26. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
  27. package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +3 -0
  28. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts +5 -4
  29. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
  30. package/dest/services/reqresp/rate-limiter/rate_limiter.js +10 -8
  31. package/dest/services/service.d.ts +7 -1
  32. package/dest/services/service.d.ts.map +1 -1
  33. package/dest/services/tx_collection/file_store_tx_source.d.ts +5 -4
  34. package/dest/services/tx_collection/file_store_tx_source.d.ts.map +1 -1
  35. package/dest/services/tx_collection/file_store_tx_source.js +39 -29
  36. package/dest/services/tx_collection/tx_source.d.ts +6 -5
  37. package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
  38. package/dest/services/tx_collection/tx_source.js +9 -7
  39. package/dest/test-helpers/mock-pubsub.d.ts +6 -1
  40. package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
  41. package/dest/test-helpers/mock-pubsub.js +9 -1
  42. package/dest/testbench/p2p_client_testbench_worker.js +41 -12
  43. package/dest/testbench/worker_client_manager.d.ts +1 -1
  44. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  45. package/dest/testbench/worker_client_manager.js +0 -1
  46. package/dest/util.d.ts +8 -3
  47. package/dest/util.d.ts.map +1 -1
  48. package/dest/util.js +2 -9
  49. package/package.json +14 -14
  50. package/src/client/factory.ts +8 -3
  51. package/src/client/test/tx_proposal_collector/proposal_tx_collector_worker.ts +15 -3
  52. package/src/index.ts +0 -1
  53. package/src/mem_pools/index.ts +0 -3
  54. package/src/mem_pools/tx_pool_v2/tx_metadata.ts +7 -1
  55. package/src/msg_validators/proposal_validator/README.md +1 -1
  56. package/src/services/discv5/discV5_service.ts +4 -2
  57. package/src/services/libp2p/libp2p_service.ts +117 -43
  58. package/src/services/peer-manager/peer_manager.ts +21 -6
  59. package/src/services/peer-manager/peer_scoring.ts +21 -5
  60. package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +3 -0
  61. package/src/services/reqresp/rate-limiter/rate_limiter.ts +13 -9
  62. package/src/services/service.ts +7 -0
  63. package/src/services/tx_collection/file_store_tx_source.ts +43 -31
  64. package/src/services/tx_collection/tx_source.ts +8 -7
  65. package/src/test-helpers/mock-pubsub.ts +9 -0
  66. package/src/testbench/p2p_client_testbench_worker.ts +40 -9
  67. package/src/testbench/worker_client_manager.ts +0 -1
  68. package/src/util.ts +8 -12
  69. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts +0 -125
  70. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +0 -1
  71. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +0 -596
  72. package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts +0 -32
  73. package/dest/mem_pools/tx_pool/eviction/eviction_manager.d.ts.map +0 -1
  74. package/dest/mem_pools/tx_pool/eviction/eviction_manager.js +0 -112
  75. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts +0 -157
  76. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.d.ts.map +0 -1
  77. package/dest/mem_pools/tx_pool/eviction/eviction_strategy.js +0 -52
  78. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts +0 -16
  79. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.d.ts.map +0 -1
  80. package/dest/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.js +0 -123
  81. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts +0 -17
  82. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.d.ts.map +0 -1
  83. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.js +0 -84
  84. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts +0 -19
  85. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.d.ts.map +0 -1
  86. package/dest/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.js +0 -78
  87. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts +0 -26
  88. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.d.ts.map +0 -1
  89. package/dest/mem_pools/tx_pool/eviction/low_priority_eviction_rule.js +0 -84
  90. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts +0 -25
  91. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.d.ts.map +0 -1
  92. package/dest/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.js +0 -57
  93. package/dest/mem_pools/tx_pool/index.d.ts +0 -3
  94. package/dest/mem_pools/tx_pool/index.d.ts.map +0 -1
  95. package/dest/mem_pools/tx_pool/index.js +0 -2
  96. package/dest/mem_pools/tx_pool/priority.d.ts +0 -12
  97. package/dest/mem_pools/tx_pool/priority.d.ts.map +0 -1
  98. package/dest/mem_pools/tx_pool/priority.js +0 -15
  99. package/dest/mem_pools/tx_pool/tx_pool.d.ts +0 -127
  100. package/dest/mem_pools/tx_pool/tx_pool.d.ts.map +0 -1
  101. package/dest/mem_pools/tx_pool/tx_pool.js +0 -3
  102. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts +0 -7
  103. package/dest/mem_pools/tx_pool/tx_pool_test_suite.d.ts.map +0 -1
  104. package/dest/mem_pools/tx_pool/tx_pool_test_suite.js +0 -402
  105. package/src/mem_pools/tx_pool/README.md +0 -270
  106. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +0 -746
  107. package/src/mem_pools/tx_pool/eviction/eviction_manager.ts +0 -132
  108. package/src/mem_pools/tx_pool/eviction/eviction_strategy.ts +0 -208
  109. package/src/mem_pools/tx_pool/eviction/fee_payer_balance_eviction_rule.ts +0 -163
  110. package/src/mem_pools/tx_pool/eviction/invalid_txs_after_mining_rule.ts +0 -104
  111. package/src/mem_pools/tx_pool/eviction/invalid_txs_after_reorg_rule.ts +0 -93
  112. package/src/mem_pools/tx_pool/eviction/low_priority_eviction_rule.ts +0 -106
  113. package/src/mem_pools/tx_pool/eviction/nullifier_conflict_pre_add_rule.ts +0 -75
  114. package/src/mem_pools/tx_pool/index.ts +0 -2
  115. package/src/mem_pools/tx_pool/priority.ts +0 -20
  116. package/src/mem_pools/tx_pool/tx_pool.ts +0 -141
  117. package/src/mem_pools/tx_pool/tx_pool_test_suite.ts +0 -321
@@ -1,5 +1,6 @@
1
1
  import { median } from '@aztec/foundation/collection';
2
2
  import { createLogger } from '@aztec/foundation/log';
3
+ import { DateProvider } from '@aztec/foundation/timer';
3
4
  import { PeerErrorSeverity } from '@aztec/stdlib/p2p';
4
5
  import {
5
6
  Attributes,
@@ -54,6 +55,7 @@ export enum PeerScoreState {
54
55
  // TODO: move into config / constants
55
56
  const MIN_SCORE_BEFORE_BAN = -100;
56
57
  const MIN_SCORE_BEFORE_DISCONNECT = -50;
58
+ const SCORE_CLEANUP_THRESHOLD = 0.1;
57
59
 
58
60
  export class PeerScoring {
59
61
  private logger = createLogger('p2p:peer-scoring');
@@ -65,7 +67,11 @@ export class PeerScoring {
65
67
 
66
68
  private peerStateCounter: UpDownCounter;
67
69
 
68
- constructor(config: P2PConfig, telemetry: TelemetryClient = getTelemetryClient()) {
70
+ constructor(
71
+ config: P2PConfig,
72
+ telemetry: TelemetryClient = getTelemetryClient(),
73
+ private readonly dateProvider: DateProvider = new DateProvider(),
74
+ ) {
69
75
  const orderedValues = config.peerPenaltyValues?.sort((a, b) => a - b);
70
76
  this.peerPenalties = {
71
77
  [PeerErrorSeverity.HighToleranceError]:
@@ -92,7 +98,7 @@ export class PeerScoring {
92
98
  }
93
99
 
94
100
  updateScore(peerId: string, scoreDelta: number): number {
95
- const currentTime = Date.now();
101
+ const currentTime = this.dateProvider.now();
96
102
  const lastUpdate = this.lastUpdateTime.get(peerId) || currentTime;
97
103
  const timePassed = currentTime - lastUpdate;
98
104
  const decayPeriods = Math.floor(timePassed / this.decayInterval);
@@ -111,19 +117,29 @@ export class PeerScoring {
111
117
  }
112
118
 
113
119
  decayAllScores(): void {
114
- const currentTime = Date.now();
120
+ const currentTime = this.dateProvider.now();
115
121
  for (const [peerId, lastUpdate] of this.lastUpdateTime.entries()) {
116
122
  const timePassed = currentTime - lastUpdate;
117
123
  const decayPeriods = Math.floor(timePassed / this.decayInterval);
118
124
  if (decayPeriods > 0) {
119
125
  let score = this.scores.get(peerId) || 0;
120
126
  score *= Math.pow(this.decayFactor, decayPeriods);
121
- this.scores.set(peerId, score);
122
- this.lastUpdateTime.set(peerId, currentTime);
127
+ if (Math.abs(score) < SCORE_CLEANUP_THRESHOLD) {
128
+ this.scores.delete(peerId);
129
+ this.lastUpdateTime.delete(peerId);
130
+ } else {
131
+ this.scores.set(peerId, score);
132
+ this.lastUpdateTime.set(peerId, currentTime);
133
+ }
123
134
  }
124
135
  }
125
136
  }
126
137
 
138
+ removePeer(peerId: string): void {
139
+ this.scores.delete(peerId);
140
+ this.lastUpdateTime.delete(peerId);
141
+ }
142
+
127
143
  getScore(peerId: string): number {
128
144
  return this.scores.get(peerId) || 0;
129
145
  }
@@ -514,6 +514,9 @@ export class BatchTxRequester {
514
514
  });
515
515
 
516
516
  if (hasInvalidTx) {
517
+ this.logger.warn(`Penalizing peer ${peerId.toString()} for sending invalid transactions in batch response`, {
518
+ peerId,
519
+ });
517
520
  this.peers.penalisePeer(peerId, PeerErrorSeverity.LowToleranceError);
518
521
  } else {
519
522
  // If we have received successful response from the peer, they have "redeemed" themselves and not considered bad anymore
@@ -97,9 +97,10 @@ export function prettyPrintRateLimitStatus(status: RateLimitStatus) {
97
97
  * 2. Individual rate limits for each peer.
98
98
  *
99
99
  * How it works:
100
- * - When a request comes in, it first checks against the global rate limit.
101
- * - If the global limit allows, it then checks against the specific peer's rate limit.
102
- * - The request is only allowed if both the global and peer-specific limits allow it.
100
+ * - When a request comes in, it first checks against the peer's individual rate limit.
101
+ * - If the peer limit allows, it then checks against the global rate limit.
102
+ * - The request is only allowed if both the peer-specific and global limits allow it.
103
+ * - Checking peer limit first ensures a rate-limited peer cannot exhaust the global quota.
103
104
  * - It automatically creates and manages rate limiters for new peers as they make requests.
104
105
  * - It periodically cleans up rate limiters for inactive peers to conserve memory.
105
106
  *
@@ -119,10 +120,6 @@ export class SubProtocolRateLimiter {
119
120
  }
120
121
 
121
122
  allow(peerId: PeerId): RateLimitStatus {
122
- if (!this.globalLimiter.allow()) {
123
- return RateLimitStatus.DeniedGlobal;
124
- }
125
-
126
123
  const peerIdStr = peerId.toString();
127
124
  let peerLimiter: PeerRateLimiter | undefined = this.peerLimiters.get(peerIdStr);
128
125
  if (!peerLimiter) {
@@ -135,10 +132,17 @@ export class SubProtocolRateLimiter {
135
132
  } else {
136
133
  peerLimiter.lastAccess = Date.now();
137
134
  }
138
- const peerLimitAllowed = peerLimiter.limiter.allow();
139
- if (!peerLimitAllowed) {
135
+
136
+ // Check peer limit first: a rate-limited peer must not consume global quota,
137
+ // otherwise one spamming peer can starve all others by exhausting the global bucket.
138
+ if (!peerLimiter.limiter.allow()) {
140
139
  return RateLimitStatus.DeniedPeer;
141
140
  }
141
+
142
+ if (!this.globalLimiter.allow()) {
143
+ return RateLimitStatus.DeniedGlobal;
144
+ }
145
+
142
146
  return RateLimitStatus.Allowed;
143
147
  }
144
148
 
@@ -196,6 +196,13 @@ export interface PeerDiscoveryService extends EventEmitter {
196
196
  on(event: 'peer:discovered', listener: (enr: ENR) => void): this;
197
197
  emit(event: 'peer:discovered', enr: ENR): boolean;
198
198
 
199
+ /**
200
+ * Event emitted when our public IP is discovered or changes via discv5 peer interactions.
201
+ * Only emitted when enrUpdate is enabled (i.e. queryForIp=true and no static p2pIp).
202
+ */
203
+ on(event: 'ip:changed', listener: (ip: string) => void): this;
204
+ emit(event: 'ip:changed', ip: string): boolean;
205
+
199
206
  getStatus(): PeerDiscoveryState;
200
207
 
201
208
  getEnr(): ENR | undefined;
@@ -1,7 +1,8 @@
1
+ import { partitionAsync } from '@aztec/foundation/collection';
1
2
  import { type Logger, createLogger } from '@aztec/foundation/log';
2
3
  import { Timer } from '@aztec/foundation/timer';
3
4
  import { type ReadOnlyFileStore, createReadOnlyFileStore } from '@aztec/stdlib/file-store';
4
- import { Tx, type TxHash } from '@aztec/stdlib/tx';
5
+ import { Tx, type TxHash, type TxValidator } from '@aztec/stdlib/tx';
5
6
  import {
6
7
  type Histogram,
7
8
  Metrics,
@@ -23,6 +24,7 @@ export class FileStoreTxSource implements TxSource {
23
24
  private readonly fileStore: ReadOnlyFileStore,
24
25
  private readonly baseUrl: string,
25
26
  private readonly basePath: string,
27
+ private readonly txValidator: TxValidator,
26
28
  private readonly log: Logger,
27
29
  telemetry: TelemetryClient,
28
30
  ) {
@@ -44,6 +46,7 @@ export class FileStoreTxSource implements TxSource {
44
46
  public static async create(
45
47
  url: string,
46
48
  basePath: string,
49
+ txValidator: TxValidator,
47
50
  log: Logger = createLogger('p2p:file_store_tx_source'),
48
51
  telemetry: TelemetryClient = getTelemetryClient(),
49
52
  ): Promise<FileStoreTxSource | undefined> {
@@ -53,7 +56,7 @@ export class FileStoreTxSource implements TxSource {
53
56
  log.warn(`Failed to create file store for URL: ${url}`);
54
57
  return undefined;
55
58
  }
56
- return new FileStoreTxSource(fileStore, url, basePath, log, telemetry);
59
+ return new FileStoreTxSource(fileStore, url, basePath, txValidator, log, telemetry);
57
60
  } catch (err) {
58
61
  log.warn(`Error creating file store for URL: ${url}`, { error: err });
59
62
  return undefined;
@@ -65,35 +68,41 @@ export class FileStoreTxSource implements TxSource {
65
68
  }
66
69
 
67
70
  public async getTxsByHash(txHashes: TxHash[]): Promise<TxSourceCollectionResult> {
68
- const invalidTxHashes: string[] = [];
71
+ const results = await Promise.all(
72
+ txHashes.map(async txHash => {
73
+ const path = `${this.basePath}/txs/${txHash.toString()}.bin`;
74
+ const timer = new Timer();
75
+ try {
76
+ const buffer = await this.fileStore.read(path);
77
+ const tx = Tx.fromBuffer(buffer);
78
+ return { tx, downloadDuration: timer.ms(), downloadSize: buffer.length };
79
+ } catch {
80
+ this.downloadsFailed.add(1);
81
+ return undefined;
82
+ }
83
+ }),
84
+ );
85
+
86
+ const txs = results.filter(tx => tx !== undefined);
87
+ const [validTxs, invalidTxs] = await partitionAsync(
88
+ txs,
89
+ async ({ tx, downloadDuration, downloadSize }): Promise<boolean> => {
90
+ const valid = await this.txValidator.validateTx(tx);
91
+ if (valid.result === 'valid') {
92
+ this.downloadsSuccess.add(1);
93
+ this.downloadDuration.record(Math.ceil(downloadDuration));
94
+ this.downloadSize.record(downloadSize);
95
+ return true;
96
+ } else {
97
+ this.downloadsFailed.add(1);
98
+ return false;
99
+ }
100
+ },
101
+ );
102
+
69
103
  return {
70
- validTxs: (
71
- await Promise.all(
72
- txHashes.map(async txHash => {
73
- const path = `${this.basePath}/txs/${txHash.toString()}.bin`;
74
- const timer = new Timer();
75
- try {
76
- const buffer = await this.fileStore.read(path);
77
- const tx = Tx.fromBuffer(buffer);
78
- if ((await tx.validateTxHash()) && txHash.equals(tx.txHash)) {
79
- this.downloadsSuccess.add(1);
80
- this.downloadDuration.record(Math.ceil(timer.ms()));
81
- this.downloadSize.record(buffer.length);
82
- return tx;
83
- } else {
84
- invalidTxHashes.push(tx.txHash.toString());
85
- this.downloadsFailed.add(1);
86
- return undefined;
87
- }
88
- } catch {
89
- // Tx not found or error reading - return undefined
90
- this.downloadsFailed.add(1);
91
- return undefined;
92
- }
93
- }),
94
- )
95
- ).filter(tx => tx !== undefined),
96
- invalidTxHashes: invalidTxHashes,
104
+ validTxs: validTxs.map(({ tx }) => tx),
105
+ invalidTxHashes: invalidTxs.map(({ tx }) => tx.getTxHash().toString()),
97
106
  };
98
107
  }
99
108
  }
@@ -109,9 +118,12 @@ export class FileStoreTxSource implements TxSource {
109
118
  export async function createFileStoreTxSources(
110
119
  urls: string[],
111
120
  basePath: string,
121
+ txValidator: TxValidator,
112
122
  log: Logger = createLogger('p2p:file_store_tx_source'),
113
123
  telemetry: TelemetryClient = getTelemetryClient(),
114
124
  ): Promise<FileStoreTxSource[]> {
115
- const sources = await Promise.all(urls.map(url => FileStoreTxSource.create(url, basePath, log, telemetry)));
125
+ const sources = await Promise.all(
126
+ urls.map(url => FileStoreTxSource.create(url, basePath, txValidator, log, telemetry)),
127
+ );
116
128
  return sources.filter((s): s is FileStoreTxSource => s !== undefined);
117
129
  }
@@ -2,7 +2,7 @@ import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
2
2
  import { protocolContractsHash } from '@aztec/protocol-contracts';
3
3
  import type { ChainConfig } from '@aztec/stdlib/config';
4
4
  import { type AztecNode, createAztecNodeClient } from '@aztec/stdlib/interfaces/client';
5
- import type { Tx, TxHash } from '@aztec/stdlib/tx';
5
+ import type { Tx, TxHash, TxValidator } from '@aztec/stdlib/tx';
6
6
  import { type ComponentsVersions, getComponentsVersionsFromConfig } from '@aztec/stdlib/versioning';
7
7
  import { makeTracedFetch } from '@aztec/telemetry-client';
8
8
 
@@ -16,12 +16,13 @@ export interface TxSource {
16
16
  export class NodeRpcTxSource implements TxSource {
17
17
  constructor(
18
18
  private readonly client: Pick<AztecNode, 'getTxsByHash'>,
19
+ private readonly txValidator: TxValidator,
19
20
  private readonly info: string,
20
21
  ) {}
21
22
 
22
- public static fromUrl(nodeUrl: string, versions: ComponentsVersions): NodeRpcTxSource {
23
+ public static fromUrl(nodeUrl: string, txValidator: TxValidator, versions: ComponentsVersions): NodeRpcTxSource {
23
24
  const client = createAztecNodeClient(nodeUrl, versions, makeTracedFetch([1, 2, 3], false));
24
- return new NodeRpcTxSource(client, nodeUrl);
25
+ return new NodeRpcTxSource(client, txValidator, nodeUrl);
25
26
  }
26
27
 
27
28
  public getInfo() {
@@ -38,8 +39,8 @@ export class NodeRpcTxSource implements TxSource {
38
39
  const invalidTxHashes: string[] = [];
39
40
  await Promise.all(
40
41
  txs.map(async tx => {
41
- const isValid = await tx.validateTxHash();
42
- if (isValid) {
42
+ const validation = await this.txValidator.validateTx(tx);
43
+ if (validation.result === 'valid') {
43
44
  validTxs.push(tx);
44
45
  } else {
45
46
  invalidTxHashes.push(tx.getTxHash().toString());
@@ -50,7 +51,7 @@ export class NodeRpcTxSource implements TxSource {
50
51
  }
51
52
  }
52
53
 
53
- export function createNodeRpcTxSources(urls: string[], chainConfig: ChainConfig) {
54
+ export function createNodeRpcTxSources(urls: string[], txValidator: TxValidator, chainConfig: ChainConfig) {
54
55
  const versions = getComponentsVersionsFromConfig(chainConfig, protocolContractsHash, getVKTreeRoot());
55
- return urls.map(url => NodeRpcTxSource.fromUrl(url, versions));
56
+ return urls.map(url => NodeRpcTxSource.fromUrl(url, txValidator, versions));
56
57
  }
@@ -15,6 +15,7 @@ import {
15
15
  type TopicValidatorResult,
16
16
  TypedEventEmitter,
17
17
  } from '@libp2p/interface';
18
+ import type { AddressManager, ConnectionManager } from '@libp2p/interface-internal';
18
19
 
19
20
  import type { P2PConfig } from '../config.js';
20
21
  import type { MemPools } from '../mem_pools/interface.js';
@@ -212,6 +213,14 @@ export class MockPubSub implements PubSubLibp2p {
212
213
  get services() {
213
214
  return {
214
215
  pubsub: this.gossipSub,
216
+ components: {
217
+ addressManager: {
218
+ removeObservedAddr: () => {},
219
+ addObservedAddr: () => {},
220
+ confirmObservedAddr: () => {},
221
+ } as unknown as AddressManager,
222
+ connectionManager: {} as unknown as ConnectionManager,
223
+ },
215
224
  };
216
225
  }
217
226
 
@@ -321,6 +321,37 @@ let workerConfig: P2PConfig | null = null;
321
321
  let workerLogger: Logger | null = null;
322
322
  let kvStore: Awaited<ReturnType<typeof openTmpStore>> | null = null;
323
323
 
324
+ async function stopWorker() {
325
+ try {
326
+ if (workerClient) {
327
+ await workerClient.stop();
328
+ workerClient = null;
329
+ }
330
+ } catch (e) {
331
+ workerLogger?.error('Error stopping worker client', e);
332
+ }
333
+ try {
334
+ if (kvStore?.close) {
335
+ await kvStore.close();
336
+ kvStore = null;
337
+ }
338
+ } catch (e) {
339
+ workerLogger?.error('Error closing kv store', e);
340
+ }
341
+ }
342
+
343
+ function gracefulExit(code: number = 0) {
344
+ try {
345
+ if (process.connected) {
346
+ process.disconnect();
347
+ }
348
+ } catch {
349
+ // IPC channel already closed
350
+ }
351
+ // Safety fallback if lingering handles prevent the event loop from draining
352
+ setTimeout(() => process.exit(code), 5000).unref();
353
+ }
354
+
324
355
  // eslint-disable-next-line @typescript-eslint/no-misused-promises
325
356
  process.on('message', async msg => {
326
357
  const {
@@ -410,13 +441,8 @@ process.on('message', async msg => {
410
441
  const cmd = msg as any;
411
442
  switch (cmd.type) {
412
443
  case 'STOP':
413
- if (workerClient) {
414
- await workerClient.stop();
415
- }
416
- if (kvStore?.close) {
417
- await kvStore.close();
418
- }
419
- process.exit(0);
444
+ await stopWorker();
445
+ gracefulExit(0);
420
446
  break;
421
447
 
422
448
  case 'SEND_TX':
@@ -493,7 +519,12 @@ process.on('message', async msg => {
493
519
  }
494
520
  }
495
521
  } catch (err: any) {
496
- process.send!({ type: 'ERROR', error: err.message });
497
- process.exit(1);
522
+ try {
523
+ process.send!({ type: 'ERROR', error: err.message });
524
+ } catch {
525
+ // IPC channel may be closed
526
+ }
527
+ await stopWorker();
528
+ gracefulExit(1);
498
529
  }
499
530
  });
@@ -72,7 +72,6 @@ class WorkerClientManager {
72
72
  destroy() {
73
73
  this.cleanup().catch((error: Error) => {
74
74
  this.logger.error('Failed to cleanup worker client manager', error);
75
- process.exit(1);
76
75
  });
77
76
  }
78
77
 
package/src/util.ts CHANGED
@@ -7,7 +7,7 @@ import type { GossipSub } from '@chainsafe/libp2p-gossipsub';
7
7
  import { generateKeyPair, marshalPrivateKey, unmarshalPrivateKey } from '@libp2p/crypto/keys';
8
8
  import type { Identify } from '@libp2p/identify';
9
9
  import type { PeerId, PrivateKey } from '@libp2p/interface';
10
- import type { ConnectionManager } from '@libp2p/interface-internal';
10
+ import type { AddressManager, ConnectionManager } from '@libp2p/interface-internal';
11
11
  import { createFromPrivKey } from '@libp2p/peer-id-factory';
12
12
  import { resolve } from 'dns/promises';
13
13
  import { promises as fs } from 'fs';
@@ -31,6 +31,10 @@ export interface PubSubLibp2p extends Pick<Libp2p, 'status' | 'start' | 'stop' |
31
31
  | 'direct'
32
32
  | 'getMeshPeers'
33
33
  > & { score: Pick<GossipSub['score'], 'score'> };
34
+ components: {
35
+ connectionManager: ConnectionManager;
36
+ addressManager: AddressManager;
37
+ };
34
38
  };
35
39
  }
36
40
 
@@ -39,6 +43,7 @@ export type FullLibp2p = Libp2p<{
39
43
  pubsub: GossipSub;
40
44
  components: {
41
45
  connectionManager: ConnectionManager;
46
+ addressManager: AddressManager;
42
47
  };
43
48
  }>;
44
49
 
@@ -102,24 +107,15 @@ function addressToMultiAddressType(address: string): 'ip4' | 'ip6' | 'dns' {
102
107
  }
103
108
  }
104
109
 
105
- export async function configureP2PClientAddresses(
106
- _config: P2PConfig & DataStoreConfig,
107
- ): Promise<P2PConfig & DataStoreConfig> {
110
+ export function configureP2PClientAddresses(_config: P2PConfig & DataStoreConfig): P2PConfig & DataStoreConfig {
108
111
  const config = { ..._config };
109
- const { p2pIp, queryForIp, p2pBroadcastPort, p2pPort } = config;
112
+ const { p2pBroadcastPort, p2pPort } = config;
110
113
 
111
114
  // If no broadcast port is provided, use the given p2p port as the broadcast port
112
115
  if (!p2pBroadcastPort) {
113
116
  config.p2pBroadcastPort = p2pPort;
114
117
  }
115
118
 
116
- // check if no announce IP was provided
117
- if (!p2pIp) {
118
- if (queryForIp) {
119
- const publicIp = await getPublicIp();
120
- config.p2pIp = publicIp;
121
- }
122
- }
123
119
  // TODO(md): guard against setting a local ip address as the announce ip
124
120
 
125
121
  return config;
@@ -1,125 +0,0 @@
1
- import { BlockNumber } from '@aztec/foundation/branded-types';
2
- import { Fr } from '@aztec/foundation/curves/bn254';
3
- import { type Logger } from '@aztec/foundation/log';
4
- import type { TypedEventEmitter } from '@aztec/foundation/types';
5
- import type { AztecAsyncKVStore } from '@aztec/kv-store';
6
- import { AztecAddress } from '@aztec/stdlib/aztec-address';
7
- import type { MerkleTreeReadOperations, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
8
- import { BlockHeader, Tx, TxHash } from '@aztec/stdlib/tx';
9
- import { type TelemetryClient } from '@aztec/telemetry-client';
10
- import { ArchiveCache } from '../../msg_validators/tx_validator/archive_cache.js';
11
- import { FeePayerTxInfo, type PendingTxInfo, type TxBlockReference, type TxPoolOperations } from './eviction/eviction_strategy.js';
12
- import type { TxPool, TxPoolEvents, TxPoolOptions } from './tx_pool.js';
13
- declare const AztecKVTxPool_base: new () => TypedEventEmitter<TxPoolEvents>;
14
- /**
15
- * KV implementation of the Transaction Pool.
16
- */
17
- export declare class AztecKVTxPool extends AztecKVTxPool_base implements TxPool, TxPoolOperations {
18
- #private;
19
- /**
20
- * Class constructor for KV TxPool. Initiates our transaction pool as an AztecMap.
21
- * @param store - A KV store for live txs in the pool.
22
- * @param archive - A KV store for archived txs.
23
- * @param telemetry - A telemetry client.
24
- * @param archivedTxLimit - The number of txs to archive.
25
- * @param log - A logger.
26
- */
27
- constructor(store: AztecAsyncKVStore, archive: AztecAsyncKVStore, worldState: WorldStateSynchronizer, telemetry?: TelemetryClient, config?: TxPoolOptions, log?: Logger);
28
- private countTxs;
29
- isEmpty(): Promise<boolean>;
30
- /**
31
- * Marks transactions as mined in a block and updates the pool state accordingly.
32
- * Removes the transactions from the pending set and adds them to the mined set.
33
- * Also evicts any transactions that become invalid after the block is mined.
34
- * @param txHashes - Array of transaction hashes that were mined
35
- * @param blockHeader - The header of the block the transactions were mined in
36
- */
37
- markAsMined(txHashes: TxHash[], blockHeader: BlockHeader): Promise<void>;
38
- markMinedAsPending(txHashes: TxHash[], latestBlock: BlockNumber): Promise<void>;
39
- getPendingTxHashes(): Promise<TxHash[]>;
40
- /**
41
- * Checks if a transaction exists in the pool and returns it.
42
- * @param txHash - The generated tx hash.
43
- * @returns The transaction, if found, 'undefined' otherwise.
44
- */
45
- getTxByHash(txHash: TxHash): Promise<Tx | undefined>;
46
- getTxsByHash(txHashes: TxHash[]): Promise<(Tx | undefined)[]>;
47
- hasTxs(txHashes: TxHash[]): Promise<boolean[]>;
48
- hasTx(txHash: TxHash): Promise<boolean>;
49
- /**
50
- * Checks if an archived tx exists and returns it.
51
- * @param txHash - The tx hash.
52
- * @returns The transaction metadata, if found, 'undefined' otherwise.
53
- */
54
- getArchivedTxByHash(txHash: TxHash): Promise<Tx | undefined>;
55
- /**
56
- * Adds a list of transactions to the pool. Duplicates are ignored.
57
- * Handles nullifier deduplication: if an incoming tx has a nullifier conflict with
58
- * existing pending txs, it will either replace them (if higher fee) or be rejected.
59
- * @param txs - An array of txs to be added to the pool.
60
- * @returns count of added transactions
61
- */
62
- addTxs(txs: Tx[], opts?: {
63
- source?: string;
64
- }): Promise<number>;
65
- /**
66
- * Deletes transactions from the pool. Tx hashes that are not present are ignored.
67
- * Mined transactions are soft-deleted with a timestamp, pending transactions are permanently deleted.
68
- * @param txHashes - An array of tx hashes to be deleted from the tx pool.
69
- * @returns Empty promise.
70
- */
71
- deleteTxs(txHashes: TxHash[], opts?: {
72
- permanently?: boolean;
73
- }): Promise<void>;
74
- private deleteMinedTx;
75
- private deletePendingTxInDbTx;
76
- /**
77
- * Gets all the transactions stored in the pool.
78
- * @returns Array of tx objects in the order they were added to the pool.
79
- */
80
- getAllTxs(): Promise<Tx[]>;
81
- /**
82
- * Gets the hashes of all transactions currently in the tx pool.
83
- * @returns An array of transaction hashes found in the tx pool.
84
- */
85
- getAllTxHashes(): Promise<TxHash[]>;
86
- getPendingTxInfos(): Promise<PendingTxInfo[]>;
87
- private getPendingTxInfo;
88
- getPendingTxsReferencingBlocks(blockHashes: Fr[]): Promise<TxBlockReference[]>;
89
- getPendingFeePayers(): Promise<AztecAddress[]>;
90
- getFeePayerTxInfos(feePayer: AztecAddress): AsyncIterable<FeePayerTxInfo>;
91
- getMinedTxHashes(): Promise<[TxHash, BlockNumber][]>;
92
- getPendingTxCount(): Promise<number>;
93
- getMinedTxCount(): Promise<number>;
94
- getTxStatus(txHash: TxHash): Promise<'pending' | 'mined' | 'deleted' | undefined>;
95
- updateConfig(cfg: TxPoolOptions): void;
96
- markTxsAsNonEvictable(txHashes: TxHash[]): Promise<void>;
97
- clearNonEvictableTxs(): Promise<void>;
98
- /**
99
- * Permanently deletes deleted mined transactions from blocks up to and including the specified block number.
100
- * @param blockNumber - Block number threshold. Deleted mined txs from this block or earlier will be permanently deleted.
101
- * @returns The number of transactions permanently deleted.
102
- */
103
- cleanupDeletedMinedTxs(blockNumber: BlockNumber): Promise<number>;
104
- /**
105
- * Creates an ArchiveCache instance.
106
- * @param db - DB for the cache to use
107
- * @returns An ArchiveCache instance
108
- */
109
- protected createArchiveCache(db: MerkleTreeReadOperations): ArchiveCache;
110
- private archiveTxs;
111
- private addPendingTxIndicesInDbTx;
112
- private removePendingTxIndicesInDbTx;
113
- /**
114
- * Returns up to `limit` lowest-priority evictable pending tx hashes without hydrating transactions.
115
- * Iterates the priority index in ascending order and skips non-evictable txs.
116
- */
117
- getLowestPriorityEvictable(limit: number): Promise<TxHash[]>;
118
- /**
119
- * Creates a PreAddPoolAccess object for use by pre-add eviction rules.
120
- * Provides read-only access to pool state during addTxs transaction.
121
- */
122
- private getPreAddPoolAccess;
123
- }
124
- export {};
125
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXp0ZWNfa3ZfdHhfcG9vbC5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL21lbV9wb29scy90eF9wb29sL2F6dGVjX2t2X3R4X3Bvb2wudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQzlELE9BQU8sRUFBRSxFQUFFLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUVwRCxPQUFPLEVBQUUsS0FBSyxNQUFNLEVBQWdCLE1BQU0sdUJBQXVCLENBQUM7QUFDbEUsT0FBTyxLQUFLLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNqRSxPQUFPLEtBQUssRUFBRSxpQkFBaUIsRUFBcUMsTUFBTSxpQkFBaUIsQ0FBQztBQUM1RixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFDM0QsT0FBTyxLQUFLLEVBQUUsd0JBQXdCLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUd4RyxPQUFPLEVBQUUsV0FBVyxFQUFFLEVBQUUsRUFBRSxNQUFNLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUMzRCxPQUFPLEVBQUUsS0FBSyxlQUFlLEVBQXNCLE1BQU0seUJBQXlCLENBQUM7QUFLbkYsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG9EQUFvRCxDQUFDO0FBR2xGLE9BQU8sRUFDTCxjQUFjLEVBQ2QsS0FBSyxhQUFhLEVBRWxCLEtBQUssZ0JBQWdCLEVBQ3JCLEtBQUssZ0JBQWdCLEVBQ3RCLE1BQU0saUNBQWlDLENBQUM7QUFPekMsT0FBTyxLQUFLLEVBQUUsTUFBTSxFQUFFLFlBQVksRUFBRSxhQUFhLEVBQUUsTUFBTSxjQUFjLENBQUM7O0FBRXhFOztHQUVHO0FBQ0gscUJBQWEsYUFDWCxTQUFRLGtCQUNSLFlBQVcsTUFBTSxFQUFFLGdCQUFnQjs7SUFrRG5DOzs7Ozs7O09BT0c7SUFDSCxZQUNFLEtBQUss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
@@ -1 +0,0 @@
1
- {"version":3,"file":"aztec_kv_tx_pool.d.ts","sourceRoot":"","sources":["../../../src/mem_pools/tx_pool/aztec_kv_tx_pool.ts"],"names":[],"mappings":"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"}