@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
package/dest/util.js CHANGED
@@ -58,22 +58,15 @@ function addressToMultiAddressType(address) {
58
58
  return 'dns';
59
59
  }
60
60
  }
61
- export async function configureP2PClientAddresses(_config) {
61
+ export function configureP2PClientAddresses(_config) {
62
62
  const config = {
63
63
  ..._config
64
64
  };
65
- const { p2pIp, queryForIp, p2pBroadcastPort, p2pPort } = config;
65
+ const { p2pBroadcastPort, p2pPort } = config;
66
66
  // If no broadcast port is provided, use the given p2p port as the broadcast port
67
67
  if (!p2pBroadcastPort) {
68
68
  config.p2pBroadcastPort = p2pPort;
69
69
  }
70
- // check if no announce IP was provided
71
- if (!p2pIp) {
72
- if (queryForIp) {
73
- const publicIp = await getPublicIp();
74
- config.p2pIp = publicIp;
75
- }
76
- }
77
70
  // TODO(md): guard against setting a local ip address as the announce ip
78
71
  return config;
79
72
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/p2p",
3
- "version": "0.0.1-commit.9ee6fcc6",
3
+ "version": "0.0.1-commit.9ef841308",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -67,17 +67,17 @@
67
67
  ]
68
68
  },
69
69
  "dependencies": {
70
- "@aztec/constants": "0.0.1-commit.9ee6fcc6",
71
- "@aztec/epoch-cache": "0.0.1-commit.9ee6fcc6",
72
- "@aztec/ethereum": "0.0.1-commit.9ee6fcc6",
73
- "@aztec/foundation": "0.0.1-commit.9ee6fcc6",
74
- "@aztec/kv-store": "0.0.1-commit.9ee6fcc6",
75
- "@aztec/noir-contracts.js": "0.0.1-commit.9ee6fcc6",
76
- "@aztec/noir-protocol-circuits-types": "0.0.1-commit.9ee6fcc6",
77
- "@aztec/protocol-contracts": "0.0.1-commit.9ee6fcc6",
78
- "@aztec/simulator": "0.0.1-commit.9ee6fcc6",
79
- "@aztec/stdlib": "0.0.1-commit.9ee6fcc6",
80
- "@aztec/telemetry-client": "0.0.1-commit.9ee6fcc6",
70
+ "@aztec/constants": "0.0.1-commit.9ef841308",
71
+ "@aztec/epoch-cache": "0.0.1-commit.9ef841308",
72
+ "@aztec/ethereum": "0.0.1-commit.9ef841308",
73
+ "@aztec/foundation": "0.0.1-commit.9ef841308",
74
+ "@aztec/kv-store": "0.0.1-commit.9ef841308",
75
+ "@aztec/noir-contracts.js": "0.0.1-commit.9ef841308",
76
+ "@aztec/noir-protocol-circuits-types": "0.0.1-commit.9ef841308",
77
+ "@aztec/protocol-contracts": "0.0.1-commit.9ef841308",
78
+ "@aztec/simulator": "0.0.1-commit.9ef841308",
79
+ "@aztec/stdlib": "0.0.1-commit.9ef841308",
80
+ "@aztec/telemetry-client": "0.0.1-commit.9ef841308",
81
81
  "@chainsafe/libp2p-gossipsub": "13.0.0",
82
82
  "@chainsafe/libp2p-noise": "^15.0.0",
83
83
  "@chainsafe/libp2p-yamux": "^6.0.2",
@@ -104,8 +104,8 @@
104
104
  "xxhash-wasm": "^1.1.0"
105
105
  },
106
106
  "devDependencies": {
107
- "@aztec/archiver": "0.0.1-commit.9ee6fcc6",
108
- "@aztec/world-state": "0.0.1-commit.9ee6fcc6",
107
+ "@aztec/archiver": "0.0.1-commit.9ef841308",
108
+ "@aztec/world-state": "0.0.1-commit.9ef841308",
109
109
  "@jest/globals": "^30.0.0",
110
110
  "@types/jest": "^30.0.0",
111
111
  "@types/node": "^22.15.17",
@@ -19,6 +19,7 @@ import type { TxPoolV2 } from '../mem_pools/tx_pool_v2/interfaces.js';
19
19
  import { AztecKVTxPoolV2 } from '../mem_pools/tx_pool_v2/tx_pool_v2.js';
20
20
  import {
21
21
  createCheckAllowedSetupCalls,
22
+ createTxValidatorForReqResponseReceivedTxs,
22
23
  createTxValidatorForTransactionsEnteringPendingTxPool,
23
24
  getDefaultAllowedSetupFunctions,
24
25
  } from '../msg_validators/index.js';
@@ -56,7 +57,7 @@ export async function createP2PClient(
56
57
  telemetry: TelemetryClient = getTelemetryClient(),
57
58
  deps: P2PClientDeps = {},
58
59
  ) {
59
- const config = await configureP2PClientAddresses({
60
+ const config = configureP2PClientAddresses({
60
61
  ...inputConfig,
61
62
  dataStoreMapSizeKb: inputConfig.p2pStoreMapSizeKb ?? inputConfig.dataStoreMapSizeKb,
62
63
  });
@@ -148,9 +149,12 @@ export async function createP2PClient(
148
149
  telemetry,
149
150
  );
150
151
 
152
+ const txValidatorForTxCollection = createTxValidatorForReqResponseReceivedTxs(proofVerifier, config);
151
153
  const nodeSources = [
152
- ...createNodeRpcTxSources(config.txCollectionNodeRpcUrls, config),
153
- ...(deps.rpcTxProviders ?? []).map((node, i) => new NodeRpcTxSource(node, `node-rpc-provider-${i}`)),
154
+ ...createNodeRpcTxSources(config.txCollectionNodeRpcUrls, txValidatorForTxCollection, config),
155
+ ...(deps.rpcTxProviders ?? []).map(
156
+ (node, i) => new NodeRpcTxSource(node, txValidatorForTxCollection, `node-rpc-provider-${i}`),
157
+ ),
154
158
  ...(deps.txCollectionNodeSources ?? []),
155
159
  ];
156
160
  if (nodeSources.length > 0) {
@@ -162,6 +166,7 @@ export async function createP2PClient(
162
166
  const fileStoreSources = await createFileStoreTxSources(
163
167
  config.txCollectionFileStoreUrls,
164
168
  txFileStoreBasePath,
169
+ txValidatorForTxCollection,
165
170
  logger.createChild('file-store-tx-source'),
166
171
  telemetry,
167
172
  );
@@ -259,9 +259,20 @@ async function stopClient() {
259
259
  attestationPool = undefined;
260
260
  }
261
261
 
262
+ function gracefulExit(code: number = 0) {
263
+ try {
264
+ if (process.connected) {
265
+ process.disconnect();
266
+ }
267
+ } catch {
268
+ // IPC channel already closed
269
+ }
270
+ setTimeout(() => process.exit(code), 5000).unref();
271
+ }
272
+
262
273
  process.on('disconnect', () => {
263
274
  ipcDisconnected = true;
264
- void stopClient().finally(() => process.exit(0));
275
+ void stopClient();
265
276
  });
266
277
 
267
278
  process.on('error', err => {
@@ -325,7 +336,7 @@ process.on('message', (msg: WorkerCommand) => {
325
336
  case 'STOP': {
326
337
  await stopClient();
327
338
  await sendMessage({ type: 'STOPPED', requestId });
328
- process.exit(0);
339
+ gracefulExit(0);
329
340
  break;
330
341
  }
331
342
  default: {
@@ -336,7 +347,8 @@ process.on('message', (msg: WorkerCommand) => {
336
347
  } catch (err: any) {
337
348
  await sendMessage({ type: 'ERROR', requestId, error: err?.message ?? String(err) });
338
349
  if (msg.type === 'START') {
339
- process.exit(1);
350
+ await stopClient();
351
+ gracefulExit(1);
340
352
  }
341
353
  }
342
354
  })();
package/src/index.ts CHANGED
@@ -6,7 +6,6 @@ export * from './client/index.js';
6
6
  export * from './enr/index.js';
7
7
  export * from './config.js';
8
8
  export * from './mem_pools/attestation_pool/index.js';
9
- export * from './mem_pools/tx_pool/index.js';
10
9
  export * from './mem_pools/tx_pool_v2/index.js';
11
10
  export * from './msg_validators/index.js';
12
11
  export * from './services/index.js';
@@ -1,6 +1,3 @@
1
1
  export { AttestationPool, type AttestationPoolApi } from './attestation_pool/attestation_pool.js';
2
2
  export { type MemPools } from './interface.js';
3
- // Old TxPool exports - kept temporarily for external consumers
4
- export { type TxPool } from './tx_pool/tx_pool.js';
5
- // New TxPoolV2 exports
6
3
  export { type TxPoolV2, type TxPoolV2Config, type TxPoolV2Events, type AddTxsResult } from './tx_pool_v2/index.js';
@@ -1,3 +1,4 @@
1
+ import { minBigint } from '@aztec/foundation/bigint';
1
2
  import { BlockNumber } from '@aztec/foundation/branded-types';
2
3
  import { Fr } from '@aztec/foundation/curves/bn254';
3
4
  import { ProtocolContractAddress } from '@aztec/protocol-contracts';
@@ -6,7 +7,6 @@ import { Gas } from '@aztec/stdlib/gas';
6
7
  import { type Tx, TxHash } from '@aztec/stdlib/tx';
7
8
 
8
9
  import { getFeePayerBalanceDelta } from '../../msg_validators/tx_validator/fee_payer_balance.js';
9
- import { getTxPriorityFee } from '../tx_pool/priority.js';
10
10
  import { type PreAddResult, TxPoolRejectionCode } from './eviction/interfaces.js';
11
11
 
12
12
  /** Validator-compatible data interface, mirroring the subset of PrivateKernelTailCircuitPublicInputs used by validators. */
@@ -335,3 +335,9 @@ export function stubTxMetaData(
335
335
  data: stubTxMetaValidationData({ expirationTimestamp }),
336
336
  };
337
337
  }
338
+
339
+ /** Returns the priority fee for a tx, based on the L2 priority fee capped by the max fee per gas. */
340
+ function getTxPriorityFee(tx: Tx): bigint {
341
+ const { maxPriorityFeesPerGas: priorityFees, maxFeesPerGas } = tx.getGasSettings();
342
+ return minBigint(maxFeesPerGas.feePerL2Gas, priorityFees.feePerL2Gas);
343
+ }
@@ -53,7 +53,7 @@ Only runs on validator nodes. Non-validator nodes use a default handler that tri
53
53
 
54
54
  **Escape hatch**: during escape hatch periods (`isEscapeHatchOpenAtSlot`), re-execution and slashing are both disabled, and the proposal is rejected locally.
55
55
 
56
- **Conditional re-execution**: rules 22-24 only run when at least one condition is true: `fishermanMode` enabled, `slashBroadcastedInvalidBlockPenalty > 0` with `validatorReexecute`, committee membership with `validatorReexecute`, `alwaysReexecuteBlockProposals`, or `blobClient.canUpload()`.
56
+ **Conditional re-execution**: rules 22-24 only run when at least one condition is true: `fishermanMode` enabled, `slashBroadcastedInvalidBlockPenalty > 0`, committee membership, `alwaysReexecuteBlockProposals`, or `blobClient.canUpload()`.
57
57
 
58
58
  **Slashing**: only `state_mismatch` and `failed_txs` trigger on-chain slashing (`OffenseType.BROADCASTED_INVALID_BLOCK_PROPOSAL`, gated by `slashBroadcastedInvalidBlockPenalty > 0`). Unknown errors during re-execution do NOT slash.
59
59
 
@@ -96,7 +96,7 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
96
96
  lookupTimeout: 2000,
97
97
  requestTimeout: 2000,
98
98
  allowUnverifiedSessions: true,
99
- enrUpdate: !p2pIp ? true : false, // If no p2p IP is set, enrUpdate can automatically resolve it
99
+ enrUpdate: config.queryForIp && !p2pIp, // Enable native ENR IP discovery when no static IP is configured
100
100
  ...configOverrides.config,
101
101
  },
102
102
  metricsRegistry,
@@ -129,9 +129,11 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
129
129
  private onMultiaddrUpdated(m: Multiaddr) {
130
130
  // We want to update our tcp port to match the udp port
131
131
  // p2pBroadcastPort is optional on config, however it is set to default within the p2p client factory
132
- const multiAddrTcp = multiaddr(convertToMultiaddr(m.nodeAddress().address, this.config.p2pBroadcastPort!, 'tcp'));
132
+ const address = m.nodeAddress().address;
133
+ const multiAddrTcp = multiaddr(convertToMultiaddr(address, this.config.p2pBroadcastPort!, 'tcp'));
133
134
  this.enr.setLocationMultiaddr(multiAddrTcp);
134
135
  this.logger.info('Multiaddr updated', { multiaddr: multiAddrTcp.toString() });
136
+ this.emit('ip:changed', address);
135
137
  }
136
138
 
137
139
  public async start(): Promise<void> {
@@ -51,9 +51,10 @@ import { yamux } from '@chainsafe/libp2p-yamux';
51
51
  import { bootstrap } from '@libp2p/bootstrap';
52
52
  import { identify } from '@libp2p/identify';
53
53
  import { type Message, type MultiaddrConnection, type PeerId, TopicValidatorResult } from '@libp2p/interface';
54
- import type { ConnectionManager } from '@libp2p/interface-internal';
54
+ import type { AddressManager, ConnectionManager } from '@libp2p/interface-internal';
55
55
  import { mplex } from '@libp2p/mplex';
56
56
  import { tcp } from '@libp2p/tcp';
57
+ import { multiaddr } from '@multiformats/multiaddr';
57
58
  import { ENR } from '@nethermindeth/enr';
58
59
  import { createLibp2p } from 'libp2p';
59
60
 
@@ -130,7 +131,7 @@ type ValidationOutcome = { allPassed: true } | { allPassed: false; failure: Vali
130
131
  // REFACTOR: Unify with the type above
131
132
  type ReceivedMessageValidationResult<T, M = undefined> =
132
133
  | { obj: T; result: Exclude<TopicValidatorResult, TopicValidatorResult.Reject>; metadata?: M }
133
- | { obj?: T; result: TopicValidatorResult.Reject; metadata?: M };
134
+ | { obj?: T; result: TopicValidatorResult.Reject; metadata?: M; severity: PeerErrorSeverity };
134
135
 
135
136
  /**
136
137
  * Lib P2P implementation of the P2PService interface.
@@ -174,6 +175,10 @@ export class LibP2PService extends WithTracer implements P2PService {
174
175
  private checkpointReceivedCallback: P2PCheckpointReceivedCallback;
175
176
 
176
177
  private gossipSubEventHandler: (e: CustomEvent<GossipsubMessage>) => void;
178
+ private ipChangedHandler?: (ip: string) => void;
179
+
180
+ /** Discovered public IP address (set when queryForIp is enabled and no static IP was configured). */
181
+ private discoveredP2pIp?: string;
177
182
 
178
183
  private instrumentation: P2PInstrumentation;
179
184
 
@@ -442,8 +447,9 @@ export class LibP2PService extends WithTracer implements P2PService {
442
447
  topics: topicScoreParams,
443
448
  }),
444
449
  }) as (components: GossipSubComponents) => GossipSub,
445
- components: (components: { connectionManager: ConnectionManager }) => ({
450
+ components: (components: { connectionManager: ConnectionManager; addressManager: AddressManager }) => ({
446
451
  connectionManager: components.connectionManager,
452
+ addressManager: components.addressManager,
447
453
  }),
448
454
  },
449
455
  logger: createLibp2pComponentLogger(logger.module, logger.getBindings()),
@@ -502,10 +508,10 @@ export class LibP2PService extends WithTracer implements P2PService {
502
508
 
503
509
  // Get listen & announce addresses for logging
504
510
  const { p2pIp, p2pPort } = this.config;
505
- if (!p2pIp) {
511
+ if (!p2pIp && !this.config.queryForIp) {
506
512
  throw new Error('Announce address not provided.');
507
513
  }
508
- const announceTcpMultiaddr = convertToMultiaddr(p2pIp, p2pPort, 'tcp');
514
+ const announceTcpMultiaddr = p2pIp ? convertToMultiaddr(p2pIp, p2pPort, 'tcp') : undefined;
509
515
 
510
516
  // Create request response protocol handlers
511
517
  const txHandler = reqRespTxHandler(this.mempools);
@@ -559,6 +565,31 @@ export class LibP2PService extends WithTracer implements P2PService {
559
565
  if (!this.config.p2pDiscoveryDisabled) {
560
566
  await this.peerDiscoveryService.start();
561
567
  }
568
+
569
+ // When queryForIp is enabled and no static IP was configured, bridge discv5 IP discovery to libp2p.
570
+ // Discv5 discovers our public IP via peer WHOAREYOU exchanges (enrUpdate=true) and emits 'ip:changed'.
571
+ // We confirm the discovered address in the libp2p AddressManager so it appears in getMultiaddrs()
572
+ // and is pushed to all connected peers via the identify protocol.
573
+ if (this.config.queryForIp && !p2pIp) {
574
+ this.ipChangedHandler = (ip: string) => {
575
+ const addressManager = this.node.services.components.addressManager;
576
+ const newAddr = multiaddr(convertToMultiaddr(ip, this.config.p2pPort, 'tcp'));
577
+
578
+ // Remove old discovered IP if one exists
579
+ if (this.discoveredP2pIp) {
580
+ const oldAddr = multiaddr(convertToMultiaddr(this.discoveredP2pIp, this.config.p2pPort, 'tcp'));
581
+ addressManager.removeObservedAddr(oldAddr);
582
+ }
583
+
584
+ addressManager.addObservedAddr(newAddr);
585
+ addressManager.confirmObservedAddr(newAddr);
586
+ // Store discovered IP
587
+ this.discoveredP2pIp = ip;
588
+ this.logger.info('Public IP discovered via discv5', { ip });
589
+ };
590
+ this.peerDiscoveryService.on('ip:changed', this.ipChangedHandler);
591
+ }
592
+
562
593
  this.discoveryRunningPromise = new RunningPromise(
563
594
  async () => {
564
595
  await this.peerManager.heartbeat();
@@ -571,7 +602,7 @@ export class LibP2PService extends WithTracer implements P2PService {
571
602
  this.logger.info(`Started P2P service`, {
572
603
  listen: this.config.listenAddress,
573
604
  port: this.config.p2pPort,
574
- announce: announceTcpMultiaddr,
605
+ announce: announceTcpMultiaddr ?? 'pending (queryForIp=true)',
575
606
  peerId: this.node.peerId.toString(),
576
607
  });
577
608
  }
@@ -584,6 +615,12 @@ export class LibP2PService extends WithTracer implements P2PService {
584
615
  // Remove gossip sub listener
585
616
  this.node.services.pubsub.removeEventListener(GossipSubEvent.MESSAGE, this.gossipSubEventHandler);
586
617
 
618
+ // Remove ip:changed listener if registered
619
+ if (this.ipChangedHandler) {
620
+ this.peerDiscoveryService.off('ip:changed', this.ipChangedHandler);
621
+ this.ipChangedHandler = undefined;
622
+ }
623
+
587
624
  // Stop peer manager
588
625
  this.logger.debug('Stopping peer manager...');
589
626
  await this.peerManager.stop();
@@ -882,30 +919,56 @@ export class LibP2PService extends WithTracer implements P2PService {
882
919
  source: PeerId,
883
920
  topicType: TopicType,
884
921
  ): Promise<ReceivedMessageValidationResult<T, M>> {
885
- let resultAndObj: ReceivedMessageValidationResult<T, M> = { result: TopicValidatorResult.Reject };
922
+ // Default to reject result with a penalty if validation function throws an error
923
+ let resultAndObj: ReceivedMessageValidationResult<T, M> = {
924
+ result: TopicValidatorResult.Reject,
925
+ severity: PeerErrorSeverity.MidToleranceError,
926
+ };
886
927
  const timer = new Timer();
887
928
  try {
888
929
  resultAndObj = await validationFunc();
889
930
  } catch (err) {
890
- this.peerManager.penalizePeer(source, PeerErrorSeverity.LowToleranceError);
891
- this.logger.error(`Error deserializing and validating gossipsub message`, err, {
892
- msgId,
893
- source: source.toString(),
894
- topicType,
895
- });
931
+ this.logger.error(`Error validating gossipsub message`, err, { msgId, source: source.toString(), topicType });
896
932
  }
897
933
 
898
934
  if (resultAndObj.result === TopicValidatorResult.Accept) {
935
+ this.logger.debug(`Message ${topicType} accepted by validator`, { msgId, source: source.toString(), topicType });
899
936
  this.instrumentation.recordMessageValidation(topicType, timer);
937
+ } else if (resultAndObj.result === TopicValidatorResult.Reject) {
938
+ this.logger.warn(`Message ${topicType} rejected by validator with severity ${resultAndObj.severity}`, {
939
+ msgId,
940
+ source: source.toString(),
941
+ topicType,
942
+ severity: resultAndObj.severity,
943
+ });
944
+ this.peerManager.penalizePeer(source, resultAndObj.severity);
945
+ } else {
946
+ this.logger.trace(`Message ${topicType} ignored by validator`, { msgId, source: source.toString(), topicType });
900
947
  }
901
948
 
902
949
  this.node.services.pubsub.reportMessageValidationResult(msgId, source.toString(), resultAndObj.result);
903
950
  return resultAndObj;
904
951
  }
905
952
 
953
+ private tryDeserialize<T>(deserializeFunc: () => T, msgId: string, source: PeerId): T | undefined {
954
+ try {
955
+ return deserializeFunc();
956
+ } catch (err) {
957
+ this.logger.warn(`Failed to deserialize gossipsub message from buffer`, {
958
+ err,
959
+ msgId,
960
+ source: source.toString(),
961
+ });
962
+ return undefined;
963
+ }
964
+ }
965
+
906
966
  protected async handleGossipedTx(payloadData: Buffer, msgId: string, source: PeerId) {
907
967
  const validationFunc: () => Promise<ReceivedMessageValidationResult<Tx>> = async () => {
908
- const tx = Tx.fromBuffer(payloadData);
968
+ const tx = this.tryDeserialize(() => Tx.fromBuffer(payloadData), msgId, source);
969
+ if (!tx) {
970
+ return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.LowToleranceError };
971
+ }
909
972
 
910
973
  const currentBlockNumber = await this.archiver.getBlockNumber();
911
974
  const { ts: nextSlotTimestamp } = this.epochCache.getEpochAndSlotInNextL1Slot();
@@ -930,8 +993,7 @@ export class LibP2PService extends WithTracer implements P2PService {
930
993
  severity,
931
994
  source: source.toString(),
932
995
  });
933
- this.peerManager.penalizePeer(source, severity);
934
- return { result: TopicValidatorResult.Reject };
996
+ return { result: TopicValidatorResult.Reject, severity };
935
997
  }
936
998
 
937
999
  // Pool pre-check: see if the pool would accept this tx before doing expensive proof verification
@@ -953,8 +1015,7 @@ export class LibP2PService extends WithTracer implements P2PService {
953
1015
  severity,
954
1016
  source: source.toString(),
955
1017
  });
956
- this.peerManager.penalizePeer(source, severity);
957
- return { result: TopicValidatorResult.Reject };
1018
+ return { result: TopicValidatorResult.Reject, severity };
958
1019
  }
959
1020
 
960
1021
  // Pool add: persist the tx
@@ -979,8 +1040,7 @@ export class LibP2PService extends WithTracer implements P2PService {
979
1040
  source: source.toString(),
980
1041
  txHash: txHash.toString(),
981
1042
  });
982
- this.peerManager.penalizePeer(source, PeerErrorSeverity.HighToleranceError);
983
- return { result: TopicValidatorResult.Reject };
1043
+ return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.HighToleranceError };
984
1044
  }
985
1045
  };
986
1046
 
@@ -1010,7 +1070,16 @@ export class LibP2PService extends WithTracer implements P2PService {
1010
1070
  source: PeerId,
1011
1071
  ): Promise<void> {
1012
1072
  const { result, obj: attestation } = await this.validateReceivedMessage<CheckpointAttestation>(
1013
- () => this.validateAndStoreCheckpointAttestation(source, CheckpointAttestation.fromBuffer(payloadData)),
1073
+ () => {
1074
+ const attestation = this.tryDeserialize(() => CheckpointAttestation.fromBuffer(payloadData), msgId, source);
1075
+ if (!attestation) {
1076
+ return Promise.resolve({
1077
+ result: TopicValidatorResult.Reject,
1078
+ severity: PeerErrorSeverity.LowToleranceError,
1079
+ });
1080
+ }
1081
+ return this.validateAndStoreCheckpointAttestation(source, attestation);
1082
+ },
1014
1083
  msgId,
1015
1084
  source,
1016
1085
  TopicType.checkpoint_attestation,
@@ -1043,8 +1112,7 @@ export class LibP2PService extends WithTracer implements P2PService {
1043
1112
 
1044
1113
  if (validationResult.result === 'reject') {
1045
1114
  this.logger.warn(`Penalizing peer ${peerId} for checkpoint attestation validation failure`);
1046
- this.peerManager.penalizePeer(peerId, validationResult.severity);
1047
- return { result: TopicValidatorResult.Reject };
1115
+ return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
1048
1116
  }
1049
1117
 
1050
1118
  if (validationResult.result === 'ignore') {
@@ -1070,16 +1138,16 @@ export class LibP2PService extends WithTracer implements P2PService {
1070
1138
  return { result: TopicValidatorResult.Ignore, obj: attestation };
1071
1139
  }
1072
1140
 
1073
- // Could not add (cap reached for signer), no need to re-broadcast
1141
+ // Could not add (cap reached for signer), penalize and do not re-broadcast
1074
1142
  if (!added) {
1075
- this.logger.warn(`Dropping checkpoint attestation due to cap`, {
1143
+ this.logger.warn(`Rejecting checkpoint attestation due to cap`, {
1076
1144
  slot: slot.toString(),
1077
1145
  archive: attestation.archive.toString(),
1078
1146
  source: peerId.toString(),
1079
1147
  attester: attestation.getSender()?.toString(),
1080
1148
  count,
1081
1149
  });
1082
- return { result: TopicValidatorResult.Ignore, obj: attestation };
1150
+ return { result: TopicValidatorResult.Reject, severity: PeerErrorSeverity.HighToleranceError };
1083
1151
  }
1084
1152
 
1085
1153
  // Check if this is a duplicate attestation (signer attested to a different proposal at the same slot)
@@ -1134,8 +1202,7 @@ export class LibP2PService extends WithTracer implements P2PService {
1134
1202
 
1135
1203
  if (validationResult.result === 'reject') {
1136
1204
  this.logger.warn(`Penalizing peer ${peerId} for block proposal validation failure`);
1137
- this.peerManager.penalizePeer(peerId, validationResult.severity);
1138
- return { result: TopicValidatorResult.Reject };
1205
+ return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
1139
1206
  }
1140
1207
 
1141
1208
  if (validationResult.result === 'ignore') {
@@ -1159,7 +1226,6 @@ export class LibP2PService extends WithTracer implements P2PService {
1159
1226
 
1160
1227
  // Too many blocks received for this slot and index, penalize peer and do not re-broadcast
1161
1228
  if (!added) {
1162
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.HighToleranceError);
1163
1229
  this.logger.warn(`Penalizing peer for block proposal exceeding per-position cap`, {
1164
1230
  ...block.toBlockInfo(),
1165
1231
  indexWithinCheckpoint: block.indexWithinCheckpoint,
@@ -1167,7 +1233,11 @@ export class LibP2PService extends WithTracer implements P2PService {
1167
1233
  proposer: block.getSender()?.toString(),
1168
1234
  source: peerId.toString(),
1169
1235
  });
1170
- return { result: TopicValidatorResult.Reject, metadata: { isEquivocated } };
1236
+ return {
1237
+ result: TopicValidatorResult.Reject,
1238
+ metadata: { isEquivocated },
1239
+ severity: PeerErrorSeverity.HighToleranceError,
1240
+ };
1171
1241
  }
1172
1242
 
1173
1243
  // If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
@@ -1260,8 +1330,7 @@ export class LibP2PService extends WithTracer implements P2PService {
1260
1330
 
1261
1331
  if (validationResult.result === 'reject') {
1262
1332
  this.logger.warn(`Penalizing peer ${peerId} for checkpoint proposal validation failure`);
1263
- this.peerManager.penalizePeer(peerId, validationResult.severity);
1264
- return { result: TopicValidatorResult.Reject };
1333
+ return { result: TopicValidatorResult.Reject, severity: validationResult.severity };
1265
1334
  }
1266
1335
 
1267
1336
  if (validationResult.result === 'ignore') {
@@ -1276,20 +1345,21 @@ export class LibP2PService extends WithTracer implements P2PService {
1276
1345
  [Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
1277
1346
  [Attributes.P2P_ID]: peerId.toString(),
1278
1347
  });
1279
- const {
1280
- result,
1281
- obj,
1282
- metadata: { isEquivocated } = {},
1283
- } = await this.validateAndStoreBlockProposal(peerId, blockProposal);
1284
- if (result === TopicValidatorResult.Reject || !obj || isEquivocated) {
1348
+ const blockProposalResult = await this.validateAndStoreBlockProposal(peerId, blockProposal);
1349
+ const { obj, metadata: { isEquivocated } = {} } = blockProposalResult;
1350
+ if (blockProposalResult.result === TopicValidatorResult.Reject || !obj || isEquivocated) {
1285
1351
  this.logger.debug(`Rejecting checkpoint due to invalid last block proposal`, {
1286
1352
  [Attributes.SLOT_NUMBER]: checkpoint.slotNumber.toString(),
1287
1353
  [Attributes.P2P_ID]: peerId.toString(),
1288
1354
  isEquivocated,
1289
- result,
1355
+ result: blockProposalResult.result,
1290
1356
  });
1291
- return { result: TopicValidatorResult.Reject };
1292
- } else if (result === TopicValidatorResult.Accept && obj && !isEquivocated) {
1357
+ return {
1358
+ result: TopicValidatorResult.Reject,
1359
+ severity:
1360
+ 'severity' in blockProposalResult ? blockProposalResult.severity : PeerErrorSeverity.MidToleranceError,
1361
+ };
1362
+ } else if (blockProposalResult.result === TopicValidatorResult.Accept && obj && !isEquivocated) {
1293
1363
  processBlock = true;
1294
1364
  }
1295
1365
  }
@@ -1316,13 +1386,17 @@ export class LibP2PService extends WithTracer implements P2PService {
1316
1386
  // Too many checkpoint proposals received for this slot, penalize peer and do not re-broadcast
1317
1387
  // Note: We still return the checkpoint obj so the lastBlock can be processed if valid
1318
1388
  if (!added) {
1319
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.HighToleranceError);
1320
1389
  this.logger.warn(`Penalizing peer for checkpoint proposal exceeding per-slot cap`, {
1321
1390
  ...checkpoint.toCheckpointInfo(),
1322
1391
  count,
1323
1392
  source: peerId.toString(),
1324
1393
  });
1325
- return { result: TopicValidatorResult.Reject, obj: checkpoint, metadata: { isEquivocated, processBlock } };
1394
+ return {
1395
+ result: TopicValidatorResult.Reject,
1396
+ obj: checkpoint,
1397
+ metadata: { isEquivocated, processBlock },
1398
+ severity: PeerErrorSeverity.HighToleranceError,
1399
+ };
1326
1400
  }
1327
1401
 
1328
1402
  // If this was a duplicate proposal, do not process it, but do invoke the duplicate callback,
@@ -226,20 +226,30 @@ export class PeerManager implements PeerManagerInterface {
226
226
  }
227
227
 
228
228
  /**
229
- * Cleans up expired timeouts.
229
+ * Cleans up expired timeouts and stale failed-auth-handshake entries.
230
230
  *
231
231
  * When peers fail to dial after a number of retries, they are temporarily timed out.
232
232
  * This function removes any peers that have been in the timed out state for too long.
233
233
  * To give them a chance to reconnect.
234
+ *
235
+ * Also evicts entries from the failed-auth-handshake map whose expiry window has passed.
236
+ * Without this, peers that probe once and never reconnect would leave their entries in the
237
+ * map forever, causing an unbounded memory leak.
234
238
  */
235
239
  private cleanupExpiredTimeouts() {
236
- // Clean up expired timeouts
237
240
  const now = this.dateProvider.now();
241
+
238
242
  for (const [peerId, timedOutPeer] of this.timedOutPeers.entries()) {
239
243
  if (now >= timedOutPeer.timeoutUntilMs) {
240
244
  this.timedOutPeers.delete(peerId);
241
245
  }
242
246
  }
247
+
248
+ for (const [id, entry] of this.failedAuthHandshakes.entries()) {
249
+ if (now - entry.lastFailureTimestamp > FAILED_AUTH_HANDSHAKE_EXPIRY_MS) {
250
+ this.failedAuthHandshakes.delete(id);
251
+ }
252
+ }
243
253
  }
244
254
 
245
255
  /**
@@ -303,15 +313,20 @@ export class PeerManager implements PeerManagerInterface {
303
313
  */
304
314
  private handleDisconnectedPeerEvent(e: CustomEvent<PeerId>) {
305
315
  const peerId = e.detail;
316
+ const peerIdStr = peerId.toString();
306
317
  this.metrics.peerDisconnected(peerId);
307
- this.logger.verbose(`Disconnected from peer ${peerId.toString()}`);
308
- const validatorAddress = this.authenticatedPeerIdToValidatorAddress.get(peerId.toString());
318
+ this.logger.verbose(`Disconnected from peer ${peerIdStr}`);
319
+ const validatorAddress = this.authenticatedPeerIdToValidatorAddress.get(peerIdStr);
309
320
  if (validatorAddress !== undefined) {
310
321
  this.logger.info(
311
- `Removing authentication for validator ${validatorAddress} at peer id ${peerId.toString()} due to disconnection`,
322
+ `Removing authentication for validator ${validatorAddress} at peer id ${peerIdStr} due to disconnection`,
312
323
  );
313
324
  this.authenticatedValidatorAddressToPeerId.delete(validatorAddress.toString());
314
- this.authenticatedPeerIdToValidatorAddress.delete(peerId.toString());
325
+ this.authenticatedPeerIdToValidatorAddress.delete(peerIdStr);
326
+ }
327
+
328
+ if (this.peerScoring.getScoreState(peerIdStr) === PeerScoreState.Healthy) {
329
+ this.peerScoring.removePeer(peerIdStr);
315
330
  }
316
331
  }
317
332