@aztec/p2p 0.75.0 → 0.76.1

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 (102) hide show
  1. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  2. package/dest/bootstrap/bootstrap.js +9 -4
  3. package/dest/client/factory.d.ts +4 -2
  4. package/dest/client/factory.d.ts.map +1 -1
  5. package/dest/client/factory.js +4 -4
  6. package/dest/config.d.ts +30 -14
  7. package/dest/config.d.ts.map +1 -1
  8. package/dest/config.js +30 -14
  9. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +2 -2
  10. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
  11. package/dest/msg_validators/attestation_validator/attestation_validator.js +1 -1
  12. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts +2 -2
  13. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts.map +1 -1
  14. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.js +1 -1
  15. package/dest/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.d.ts +2 -2
  16. package/dest/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.d.ts.map +1 -1
  17. package/dest/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.js +1 -1
  18. package/dest/services/discv5/discV5_service.d.ts +5 -1
  19. package/dest/services/discv5/discV5_service.d.ts.map +1 -1
  20. package/dest/services/discv5/discV5_service.js +65 -18
  21. package/dest/services/dummy_service.d.ts +1 -0
  22. package/dest/services/dummy_service.d.ts.map +1 -1
  23. package/dest/services/dummy_service.js +2 -1
  24. package/dest/services/libp2p/libp2p_logger.d.ts +7 -0
  25. package/dest/services/libp2p/libp2p_logger.d.ts.map +1 -0
  26. package/dest/services/libp2p/libp2p_logger.js +67 -0
  27. package/dest/services/libp2p/libp2p_service.d.ts +3 -3
  28. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  29. package/dest/services/libp2p/libp2p_service.js +42 -10
  30. package/dest/services/reqresp/interface.d.ts +9 -0
  31. package/dest/services/reqresp/interface.d.ts.map +1 -1
  32. package/dest/services/reqresp/interface.js +1 -1
  33. package/dest/services/reqresp/protocols/goodbye.js +2 -2
  34. package/dest/services/reqresp/rate-limiter/rate_limiter.d.ts.map +1 -1
  35. package/dest/services/reqresp/rate-limiter/rate_limiter.js +4 -2
  36. package/dest/services/reqresp/rate-limiter/rate_limits.js +3 -3
  37. package/dest/services/reqresp/reqresp.d.ts +7 -2
  38. package/dest/services/reqresp/reqresp.d.ts.map +1 -1
  39. package/dest/services/reqresp/reqresp.js +90 -21
  40. package/dest/services/reqresp/status.d.ts +31 -0
  41. package/dest/services/reqresp/status.d.ts.map +1 -0
  42. package/dest/services/reqresp/status.js +52 -0
  43. package/dest/services/service.d.ts +1 -0
  44. package/dest/services/service.d.ts.map +1 -1
  45. package/dest/services/types.d.ts +1 -7
  46. package/dest/services/types.d.ts.map +1 -1
  47. package/dest/services/types.js +2 -10
  48. package/dest/test-helpers/generate-peer-id-private-keys.d.ts +7 -0
  49. package/dest/test-helpers/generate-peer-id-private-keys.d.ts.map +1 -0
  50. package/dest/test-helpers/generate-peer-id-private-keys.js +15 -0
  51. package/dest/test-helpers/get-ports.d.ts +7 -0
  52. package/dest/test-helpers/get-ports.d.ts.map +1 -0
  53. package/dest/test-helpers/get-ports.js +8 -0
  54. package/dest/test-helpers/index.d.ts +6 -0
  55. package/dest/test-helpers/index.d.ts.map +1 -0
  56. package/dest/test-helpers/index.js +6 -0
  57. package/dest/test-helpers/make-enrs.d.ts +16 -0
  58. package/dest/test-helpers/make-enrs.d.ts.map +1 -0
  59. package/dest/test-helpers/make-enrs.js +35 -0
  60. package/dest/test-helpers/make-test-p2p-clients.d.ts +37 -0
  61. package/dest/test-helpers/make-test-p2p-clients.d.ts.map +1 -0
  62. package/dest/test-helpers/make-test-p2p-clients.js +71 -0
  63. package/dest/{mocks/index.d.ts → test-helpers/reqresp-nodes.d.ts} +6 -5
  64. package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -0
  65. package/dest/test-helpers/reqresp-nodes.js +183 -0
  66. package/dest/testbench/p2p_client_testbench_worker.d.ts +2 -0
  67. package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -0
  68. package/dest/testbench/p2p_client_testbench_worker.js +125 -0
  69. package/dest/versioning.d.ts +12 -0
  70. package/dest/versioning.d.ts.map +1 -0
  71. package/dest/versioning.js +38 -0
  72. package/package.json +10 -8
  73. package/src/bootstrap/bootstrap.ts +9 -3
  74. package/src/client/factory.ts +12 -5
  75. package/src/config.ts +56 -29
  76. package/src/msg_validators/attestation_validator/attestation_validator.ts +3 -3
  77. package/src/msg_validators/block_proposal_validator/block_proposal_validator.ts +3 -3
  78. package/src/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.ts +3 -3
  79. package/src/services/discv5/discV5_service.ts +67 -18
  80. package/src/services/dummy_service.ts +2 -0
  81. package/src/services/libp2p/libp2p_logger.ts +78 -0
  82. package/src/services/libp2p/libp2p_service.ts +47 -10
  83. package/src/services/reqresp/interface.ts +11 -0
  84. package/src/services/reqresp/protocols/goodbye.ts +1 -1
  85. package/src/services/reqresp/rate-limiter/rate_limiter.ts +3 -1
  86. package/src/services/reqresp/rate-limiter/rate_limits.ts +2 -2
  87. package/src/services/reqresp/reqresp.ts +120 -25
  88. package/src/services/reqresp/status.ts +59 -0
  89. package/src/services/service.ts +2 -0
  90. package/src/services/types.ts +2 -10
  91. package/src/test-helpers/generate-peer-id-private-keys.ts +15 -0
  92. package/src/test-helpers/get-ports.ts +8 -0
  93. package/src/test-helpers/index.ts +5 -0
  94. package/src/test-helpers/make-enrs.ts +44 -0
  95. package/src/test-helpers/make-test-p2p-clients.ts +124 -0
  96. package/src/{mocks/index.ts → test-helpers/reqresp-nodes.ts} +10 -5
  97. package/src/testbench/README.md +20 -0
  98. package/src/testbench/p2p_client_testbench_worker.ts +156 -0
  99. package/src/testbench/scripts/run_testbench.sh +7 -0
  100. package/src/versioning.ts +50 -0
  101. package/dest/mocks/index.d.ts.map +0 -1
  102. package/dest/mocks/index.js +0 -181
package/src/config.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type ChainConfig, chainConfigMappings } from '@aztec/circuit-types/config';
1
2
  import {
2
3
  type ConfigMappingsType,
3
4
  booleanConfigHelper,
@@ -13,7 +14,7 @@ import { type P2PReqRespConfig, p2pReqRespConfigMappings } from './services/reqr
13
14
  /**
14
15
  * P2P client configuration values.
15
16
  */
16
- export interface P2PConfig extends P2PReqRespConfig {
17
+ export interface P2PConfig extends P2PReqRespConfig, ChainConfig {
17
18
  /**
18
19
  * A flag dictating whether the P2P subsystem should be enabled.
19
20
  */
@@ -29,6 +30,16 @@ export interface P2PConfig extends P2PReqRespConfig {
29
30
  */
30
31
  blockRequestBatchSize: number;
31
32
 
33
+ /**
34
+ * DEBUG: Disable message validation - for testing purposes only
35
+ */
36
+ debugDisableMessageValidation: boolean;
37
+
38
+ /**
39
+ * DEBUG: Disable colocation penalty - for testing purposes only
40
+ */
41
+ debugDisableColocationPenalty: boolean;
42
+
32
43
  /**
33
44
  * The frequency in which to check for new peers.
34
45
  */
@@ -69,16 +80,14 @@ export interface P2PConfig extends P2PReqRespConfig {
69
80
  */
70
81
  bootstrapNodes: string[];
71
82
 
83
+ /** Whether to execute the version check in the bootstrap node ENR. */
84
+ bootstrapNodeEnrVersionCheck: boolean;
85
+
72
86
  /**
73
87
  * Protocol identifier for transaction gossiping.
74
88
  */
75
89
  transactionProtocol: string;
76
90
 
77
- /**
78
- * The minimum number of peers (a peer count below this will cause the node to look for more peers)
79
- */
80
- minPeerCount: number;
81
-
82
91
  /**
83
92
  * The maximum number of peers (a peer count above this will cause the node to refuse connection attempts)
84
93
  */
@@ -115,6 +124,16 @@ export interface P2PConfig extends P2PReqRespConfig {
115
124
  */
116
125
  gossipsubDhi: number;
117
126
 
127
+ /**
128
+ * The Dlazy parameter for the gossipsub protocol.
129
+ */
130
+ gossipsubDLazy: number;
131
+
132
+ /**
133
+ * Whether to flood publish messages. - For testing purposes only
134
+ */
135
+ gossipsubFloodPublish: boolean;
136
+
118
137
  /**
119
138
  * The number of gossipsub interval message cache windows to keep.
120
139
  */
@@ -150,11 +169,6 @@ export interface P2PConfig extends P2PReqRespConfig {
150
169
  */
151
170
  peerPenaltyValues: number[];
152
171
 
153
- /**
154
- * The chain id of the L1 chain.
155
- */
156
- l1ChainId: number;
157
-
158
172
  /** Limit of transactions to archive in the tx pool. Once the archived tx limit is reached, the oldest archived txs will be purged. */
159
173
  archivedTxLimit: number;
160
174
  }
@@ -170,6 +184,16 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
170
184
  description: 'The frequency in which to check for new L2 blocks.',
171
185
  ...numberConfigHelper(100),
172
186
  },
187
+ debugDisableMessageValidation: {
188
+ env: 'DEBUG_P2P_DISABLE_MESSAGE_VALIDATION',
189
+ description: 'DEBUG: Disable message validation - NEVER set to true in production',
190
+ ...booleanConfigHelper(false),
191
+ },
192
+ debugDisableColocationPenalty: {
193
+ env: 'DEBUG_P2P_DISABLE_COLOCATION_PENALTY',
194
+ description: 'DEBUG: Disable colocation penalty - NEVER set to true in production',
195
+ ...booleanConfigHelper(false),
196
+ },
173
197
  peerCheckIntervalMS: {
174
198
  env: 'P2P_PEER_CHECK_INTERVAL_MS',
175
199
  description: 'The frequency in which to check for new peers.',
@@ -209,16 +233,16 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
209
233
  parseEnv: (val: string) => val.split(','),
210
234
  description: 'A list of bootstrap peer ENRs to connect to. Separated by commas.',
211
235
  },
236
+ bootstrapNodeEnrVersionCheck: {
237
+ env: 'P2P_BOOTSTRAP_NODE_ENR_VERSION_CHECK',
238
+ description: 'Whether to check the version of the bootstrap node ENR.',
239
+ ...booleanConfigHelper(),
240
+ },
212
241
  transactionProtocol: {
213
242
  env: 'P2P_TX_PROTOCOL',
214
243
  description: 'Protocol identifier for transaction gossiping.',
215
244
  defaultValue: '/aztec/0.1.0',
216
245
  },
217
- minPeerCount: {
218
- env: 'P2P_MIN_PEERS',
219
- description: 'The minimum number of peers to connect to.',
220
- ...numberConfigHelper(10),
221
- },
222
246
  maxPeerCount: {
223
247
  env: 'P2P_MAX_PEERS',
224
248
  description: 'The maximum number of peers to connect to.',
@@ -244,7 +268,7 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
244
268
  gossipsubInterval: {
245
269
  env: 'P2P_GOSSIPSUB_INTERVAL_MS',
246
270
  description: 'The interval of the gossipsub heartbeat to perform maintenance tasks.',
247
- ...numberConfigHelper(1_000),
271
+ ...numberConfigHelper(700),
248
272
  },
249
273
  gossipsubD: {
250
274
  env: 'P2P_GOSSIPSUB_D',
@@ -261,10 +285,20 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
261
285
  description: 'The Dhi parameter for the gossipsub protocol.',
262
286
  ...numberConfigHelper(12),
263
287
  },
288
+ gossipsubDLazy: {
289
+ env: 'P2P_GOSSIPSUB_DLAZY',
290
+ description: 'The Dlazy parameter for the gossipsub protocol.',
291
+ ...numberConfigHelper(6),
292
+ },
293
+ gossipsubFloodPublish: {
294
+ env: 'P2P_GOSSIPSUB_FLOOD_PUBLISH',
295
+ description: 'Whether to flood publish messages. - For testing purposes only',
296
+ ...booleanConfigHelper(true),
297
+ },
264
298
  gossipsubMcacheLength: {
265
299
  env: 'P2P_GOSSIPSUB_MCACHE_LENGTH',
266
300
  description: 'The number of gossipsub interval message cache windows to keep.',
267
- ...numberConfigHelper(5),
301
+ ...numberConfigHelper(6),
268
302
  },
269
303
  gossipsubMcacheGossip: {
270
304
  env: 'P2P_GOSSIPSUB_MCACHE_GOSSIP',
@@ -298,11 +332,6 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
298
332
  description: 'The "age" (in L2 blocks) of a tx after which we heavily penalize a peer for sending it.',
299
333
  ...numberConfigHelper(30),
300
334
  },
301
- l1ChainId: {
302
- env: 'L1_CHAIN_ID',
303
- description: 'The chain id of the L1 chain.',
304
- ...numberConfigHelper(31337),
305
- },
306
335
  blockRequestBatchSize: {
307
336
  env: 'P2P_BLOCK_REQUEST_BATCH_SIZE',
308
337
  description: 'The number of blocks to fetch in a single batch.',
@@ -315,6 +344,7 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
315
344
  ...numberConfigHelper(0),
316
345
  },
317
346
  ...p2pReqRespConfigMappings,
347
+ ...chainConfigMappings,
318
348
  };
319
349
 
320
350
  /**
@@ -332,17 +362,14 @@ export function getP2PDefaultConfig(): P2PConfig {
332
362
  /**
333
363
  * Required P2P config values for a bootstrap node.
334
364
  */
335
- export type BootnodeConfig = Pick<
336
- P2PConfig,
337
- 'udpAnnounceAddress' | 'peerIdPrivateKey' | 'minPeerCount' | 'maxPeerCount'
338
- > &
365
+ export type BootnodeConfig = Pick<P2PConfig, 'udpAnnounceAddress' | 'peerIdPrivateKey' | 'maxPeerCount'> &
339
366
  Required<Pick<P2PConfig, 'udpListenAddress'>> &
340
- Pick<DataStoreConfig, 'dataDirectory' | 'dataStoreMapSizeKB'>;
367
+ Pick<DataStoreConfig, 'dataDirectory' | 'dataStoreMapSizeKB'> &
368
+ ChainConfig;
341
369
 
342
370
  const bootnodeConfigKeys: (keyof BootnodeConfig)[] = [
343
371
  'udpAnnounceAddress',
344
372
  'peerIdPrivateKey',
345
- 'minPeerCount',
346
373
  'maxPeerCount',
347
374
  'udpListenAddress',
348
375
  'dataDirectory',
@@ -1,10 +1,10 @@
1
1
  import { type BlockAttestation, type P2PValidator, PeerErrorSeverity } from '@aztec/circuit-types';
2
- import { type EpochCache } from '@aztec/epoch-cache';
2
+ import { type EpochCacheInterface } from '@aztec/epoch-cache';
3
3
 
4
4
  export class AttestationValidator implements P2PValidator<BlockAttestation> {
5
- private epochCache: EpochCache;
5
+ private epochCache: EpochCacheInterface;
6
6
 
7
- constructor(epochCache: EpochCache) {
7
+ constructor(epochCache: EpochCacheInterface) {
8
8
  this.epochCache = epochCache;
9
9
  }
10
10
 
@@ -1,10 +1,10 @@
1
1
  import { type BlockProposal, type P2PValidator, PeerErrorSeverity } from '@aztec/circuit-types';
2
- import { type EpochCache } from '@aztec/epoch-cache';
2
+ import { type EpochCacheInterface } from '@aztec/epoch-cache';
3
3
 
4
4
  export class BlockProposalValidator implements P2PValidator<BlockProposal> {
5
- private epochCache: EpochCache;
5
+ private epochCache: EpochCacheInterface;
6
6
 
7
- constructor(epochCache: EpochCache) {
7
+ constructor(epochCache: EpochCacheInterface) {
8
8
  this.epochCache = epochCache;
9
9
  }
10
10
 
@@ -1,10 +1,10 @@
1
1
  import { type EpochProofQuote, type P2PValidator, PeerErrorSeverity } from '@aztec/circuit-types';
2
- import { type EpochCache } from '@aztec/epoch-cache';
2
+ import { type EpochCacheInterface } from '@aztec/epoch-cache';
3
3
 
4
4
  export class EpochProofQuoteValidator implements P2PValidator<EpochProofQuote> {
5
- private epochCache: EpochCache;
5
+ private epochCache: EpochCacheInterface;
6
6
 
7
- constructor(epochCache: EpochCache) {
7
+ constructor(epochCache: EpochCacheInterface) {
8
8
  this.epochCache = epochCache;
9
9
  }
10
10
 
@@ -1,3 +1,4 @@
1
+ import { type ComponentsVersions, checkCompressedComponentVersion } from '@aztec/circuit-types';
1
2
  import { createLogger } from '@aztec/foundation/log';
2
3
  import { sleep } from '@aztec/foundation/sleep';
3
4
  import { OtelMetricsAdapter, type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client';
@@ -10,8 +11,9 @@ import EventEmitter from 'events';
10
11
 
11
12
  import type { P2PConfig } from '../../config.js';
12
13
  import { convertToMultiaddr } from '../../util.js';
14
+ import { setAztecEnrKey } from '../../versioning.js';
13
15
  import { type PeerDiscoveryService, PeerDiscoveryState } from '../service.js';
14
- import { AZTEC_ENR_KEY, AZTEC_NET, Discv5Event, PeerEvent } from '../types.js';
16
+ import { AZTEC_ENR_KEY, Discv5Event, PeerEvent } from '../types.js';
15
17
 
16
18
  const delayBeforeStart = 2000; // 2sec
17
19
 
@@ -25,29 +27,32 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
25
27
  /** This instance's ENR */
26
28
  private enr: SignableENR;
27
29
 
30
+ /** Version identifiers. */
31
+ private versions: ComponentsVersions;
32
+
28
33
  /** UDP listen addr */
29
34
  private listenMultiAddrUdp: Multiaddr;
30
35
 
31
36
  private currentState = PeerDiscoveryState.STOPPED;
32
37
 
33
- private bootstrapNodes: string[];
38
+ public readonly bootstrapNodes: string[] = [];
34
39
  private bootstrapNodePeerIds: PeerId[] = [];
35
40
 
36
41
  private startTime = 0;
37
42
 
38
43
  constructor(
39
44
  private peerId: PeerId,
40
- config: P2PConfig,
45
+ private config: P2PConfig,
41
46
  telemetry: TelemetryClient = getTelemetryClient(),
42
47
  private logger = createLogger('p2p:discv5_service'),
43
48
  ) {
44
49
  super();
45
50
  const { tcpAnnounceAddress, udpAnnounceAddress, udpListenAddress, bootstrapNodes } = config;
46
- this.bootstrapNodes = bootstrapNodes;
51
+ this.bootstrapNodes = bootstrapNodes ?? [];
47
52
  // create ENR from PeerId
48
53
  this.enr = SignableENR.createFromPeerId(peerId);
49
54
  // Add aztec identification to ENR
50
- this.enr.set(AZTEC_ENR_KEY, Uint8Array.from([AZTEC_NET]));
55
+ this.versions = setAztecEnrKey(this.enr, config);
51
56
 
52
57
  if (!tcpAnnounceAddress) {
53
58
  throw new Error('You need to provide at least a TCP announce address.');
@@ -78,6 +83,20 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
78
83
  metricsRegistry,
79
84
  });
80
85
 
86
+ // Hook onto the onEstablished method to check the peer's version from the ENR,
87
+ // so we don't add it to our dht if it doesn't have the correct version.
88
+ // In addition, we'll hook onto onDiscovered to to repeat the same check there,
89
+ // just in case. Note that not adding the peer to the dht could lead to it
90
+ // being "readded" constantly, we'll need to keep an eye on whether this
91
+ // turns out to be a problem or not.
92
+ const origOnEstablished = this.discv5.onEstablished.bind(this.discv5);
93
+ this.discv5.onEstablished = (...args: unknown[]) => {
94
+ const enr = args[1] as ENR;
95
+ if (this.validateEnr(enr)) {
96
+ return origOnEstablished(...args);
97
+ }
98
+ };
99
+
81
100
  this.discv5.on(Discv5Event.DISCOVERED, this.onDiscovered.bind(this));
82
101
  this.discv5.on(Discv5Event.ENR_ADDED, this.onEnrAdded.bind(this));
83
102
  }
@@ -95,20 +114,29 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
95
114
  peerId: this.peerId,
96
115
  enrUdp: await this.enr.getFullMultiaddr('udp'),
97
116
  enrTcp: await this.enr.getFullMultiaddr('tcp'),
117
+ versions: this.versions,
98
118
  });
99
119
  this.currentState = PeerDiscoveryState.RUNNING;
100
120
 
101
121
  // Add bootnode ENR if provided
102
122
  if (this.bootstrapNodes?.length) {
103
123
  // Do this conversion once since it involves an async function call
104
- this.bootstrapNodePeerIds = await Promise.all(this.bootstrapNodes.map(enr => ENR.decodeTxt(enr).peerId()));
105
- this.logger.info(`Adding bootstrap nodes ENRs: ${this.bootstrapNodes.join(', ')}`);
106
- try {
107
- this.bootstrapNodes.forEach(enr => {
124
+ const bootstrapNodesEnrs = this.bootstrapNodes.map(enr => ENR.decodeTxt(enr));
125
+ this.bootstrapNodePeerIds = await Promise.all(bootstrapNodesEnrs.map(enr => enr.peerId()));
126
+ this.logger.info(`Adding ${this.bootstrapNodes} bootstrap nodes ENRs: ${this.bootstrapNodes.join(', ')}`);
127
+ for (const enr of bootstrapNodesEnrs) {
128
+ try {
129
+ if (this.config.bootstrapNodeEnrVersionCheck) {
130
+ const value = enr.kvs.get(AZTEC_ENR_KEY);
131
+ if (!value) {
132
+ throw new Error('ENR does not contain aztec key');
133
+ }
134
+ checkCompressedComponentVersion(Buffer.from(value).toString(), this.versions);
135
+ }
108
136
  this.discv5.addEnr(enr);
109
- });
110
- } catch (e) {
111
- this.logger.error(`Error adding bootnode ENRs: ${e}`);
137
+ } catch (e) {
138
+ this.logger.error(`Error adding bootratrap node ${enr.encodeTxt()}`, e);
139
+ }
112
140
  }
113
141
  }
114
142
  }
@@ -169,14 +197,35 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
169
197
  }
170
198
 
171
199
  private onDiscovered(enr: ENR) {
172
- // check the peer is an aztec peer
200
+ if (this.validateEnr(enr)) {
201
+ this.emit(PeerEvent.DISCOVERED, enr);
202
+ }
203
+ }
204
+
205
+ private validateEnr(enr: ENR): boolean {
206
+ // Check the peer is an aztec peer
173
207
  const value = enr.kvs.get(AZTEC_ENR_KEY);
174
- if (value) {
175
- const network = value[0];
176
- // check if the peer is on the same network
177
- if (network === AZTEC_NET) {
178
- this.emit(PeerEvent.DISCOVERED, enr);
208
+ if (!value) {
209
+ this.logger.warn(`Peer ${enr.nodeId} does not have aztec key in ENR`);
210
+ return false;
211
+ }
212
+
213
+ let compressedVersion;
214
+ try {
215
+ // And check it has the correct version
216
+ compressedVersion = Buffer.from(value).toString();
217
+ checkCompressedComponentVersion(compressedVersion, this.versions);
218
+ return true;
219
+ } catch (err: any) {
220
+ if (err.name === 'ComponentsVersionsError') {
221
+ this.logger.warn(`Peer ${enr.nodeId} has incorrect version: ${err.message}`, {
222
+ compressedVersion,
223
+ expected: this.versions,
224
+ });
225
+ } else {
226
+ this.logger.error(`Error checking peer version`, err);
179
227
  }
180
228
  }
229
+ return false;
181
230
  }
182
231
  }
@@ -88,6 +88,8 @@ export class DummyP2PService implements P2PService {
88
88
  */
89
89
  export class DummyPeerDiscoveryService extends EventEmitter implements PeerDiscoveryService {
90
90
  private currentState = PeerDiscoveryState.STOPPED;
91
+ public bootstrapNodes: string[] = [];
92
+
91
93
  /**
92
94
  * Starts the dummy implementation.
93
95
  * @returns A resolved promise.
@@ -0,0 +1,78 @@
1
+ import { createLogger } from '@aztec/foundation/log';
2
+
3
+ import { type ComponentLogger, type Logger } from '@libp2p/interface';
4
+
5
+ /**
6
+ * Creates a libp2p compatible logger that wraps our pino logger.
7
+ * This adapter implements the ComponentLogger interface required by libp2p.
8
+ */
9
+ export function createLibp2pComponentLogger(namespace: string, fixedTerms = {}): ComponentLogger {
10
+ return {
11
+ forComponent: (component: string) => createLibp2pLogger(`${namespace}:${component}`, fixedTerms),
12
+ };
13
+ }
14
+
15
+ function createLibp2pLogger(component: string, fixedTerms = {}): Logger {
16
+ const logger = createLogger(component, fixedTerms);
17
+
18
+ // Default log level is trace as this is super super noisy
19
+ const logFn = (formatter: any, ...args: any[]) => {
20
+ // Handle %p format specifier by manually replacing with args
21
+ if (typeof formatter === 'string' && args.length > 0) {
22
+ // Handle %p, %a, %s and %d format specifiers
23
+ const parts = formatter.split(/(%p|%a|%s|%d)/);
24
+ let result = parts[0];
25
+ let argIndex = 0;
26
+
27
+ for (let i = 1; i < parts.length; i += 2) {
28
+ if (argIndex < args.length) {
29
+ result += String(args[argIndex]) + (parts[i + 1] || '');
30
+ argIndex++;
31
+ }
32
+ }
33
+
34
+ formatter = result;
35
+ // Only keep non-format args as data
36
+ args = args.slice(argIndex);
37
+ }
38
+
39
+ // Handle object args by spreading them, but only if they weren't used in formatting
40
+ if (args.length === 1 && typeof args[0] === 'object') {
41
+ logger.trace(formatter, args[0]);
42
+ } else if (args.length > 0) {
43
+ // If we have remaining args after formatting, pass them as data
44
+ logger.trace(formatter, { _args: args });
45
+ } else {
46
+ logger.trace(formatter);
47
+ }
48
+ };
49
+
50
+ return Object.assign(logFn, {
51
+ enabled: logger.isLevelEnabled('debug'),
52
+
53
+ error(...args: any[]) {
54
+ const [msg, ...rest] = args;
55
+ logger.error(msg as string, ...rest);
56
+ },
57
+
58
+ debug(...args: any[]) {
59
+ const [msg, ...rest] = args;
60
+ logger.debug(msg as string, ...rest);
61
+ },
62
+
63
+ info(...args: any[]) {
64
+ const [msg, ...rest] = args;
65
+ logger.info(msg as string, ...rest);
66
+ },
67
+
68
+ warn(...args: any[]) {
69
+ const [msg, ...rest] = args;
70
+ logger.warn(msg as string, ...rest);
71
+ },
72
+
73
+ trace(...args: any[]) {
74
+ const [msg, ...rest] = args;
75
+ logger.trace(msg as string, ...rest);
76
+ },
77
+ });
78
+ }
@@ -19,7 +19,7 @@ import {
19
19
  metricsTopicStrToLabels,
20
20
  } from '@aztec/circuit-types';
21
21
  import { Fr } from '@aztec/circuits.js';
22
- import { type EpochCache } from '@aztec/epoch-cache';
22
+ import { type EpochCacheInterface } from '@aztec/epoch-cache';
23
23
  import { createLogger } from '@aztec/foundation/log';
24
24
  import { SerialQueue } from '@aztec/foundation/queue';
25
25
  import { RunningPromise } from '@aztec/foundation/running-promise';
@@ -34,8 +34,10 @@ import {
34
34
  gossipsub,
35
35
  } from '@chainsafe/libp2p-gossipsub';
36
36
  import { createPeerScoreParams, createTopicScoreParams } from '@chainsafe/libp2p-gossipsub/score';
37
+ import { SignaturePolicy } from '@chainsafe/libp2p-gossipsub/types';
37
38
  import { noise } from '@chainsafe/libp2p-noise';
38
39
  import { yamux } from '@chainsafe/libp2p-yamux';
40
+ import { bootstrap } from '@libp2p/bootstrap';
39
41
  import { identify } from '@libp2p/identify';
40
42
  import { type Message, type PeerId, TopicValidatorResult } from '@libp2p/interface';
41
43
  import { type ConnectionManager } from '@libp2p/interface-internal';
@@ -65,6 +67,7 @@ import { pingHandler, reqRespBlockHandler, reqRespTxHandler, statusHandler } fro
65
67
  import { ReqResp } from '../reqresp/reqresp.js';
66
68
  import type { P2PService, PeerDiscoveryService } from '../service.js';
67
69
  import { GossipSubEvent } from '../types.js';
70
+ import { createLibp2pComponentLogger } from './libp2p_logger.js';
68
71
 
69
72
  interface MessageValidator {
70
73
  validator: {
@@ -111,7 +114,7 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
111
114
  private peerDiscoveryService: PeerDiscoveryService,
112
115
  private mempools: MemPools<T>,
113
116
  private l2BlockSource: L2BlockSource,
114
- epochCache: EpochCache,
117
+ epochCache: EpochCacheInterface,
115
118
  private proofVerifier: ClientProtocolCircuitVerifier,
116
119
  private worldStateSynchronizer: WorldStateSynchronizer,
117
120
  telemetry: TelemetryClient,
@@ -127,7 +130,7 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
127
130
  peerDiscoveryService,
128
131
  config,
129
132
  telemetry,
130
- logger,
133
+ createLogger(`${logger.module}:peer_manager`),
131
134
  peerScoring,
132
135
  this.reqresp,
133
136
  );
@@ -164,13 +167,14 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
164
167
  peerId: PeerId,
165
168
  mempools: MemPools<T>,
166
169
  l2BlockSource: L2BlockSource,
167
- epochCache: EpochCache,
170
+ epochCache: EpochCacheInterface,
168
171
  proofVerifier: ClientProtocolCircuitVerifier,
169
172
  worldStateSynchronizer: WorldStateSynchronizer,
170
173
  store: AztecAsyncKVStore,
171
174
  telemetry: TelemetryClient,
175
+ logger = createLogger('p2p:libp2p_service'),
172
176
  ) {
173
- const { tcpListenAddress, tcpAnnounceAddress, minPeerCount, maxPeerCount } = config;
177
+ const { tcpListenAddress, tcpAnnounceAddress, maxPeerCount } = config;
174
178
  const bindAddrTcp = convertToMultiaddr(tcpListenAddress, 'tcp');
175
179
  // We know tcpAnnounceAddress cannot be null here because we set it or throw when setting up the service.
176
180
  const announceAddrTcp = convertToMultiaddr(tcpAnnounceAddress!, 'tcp');
@@ -179,6 +183,12 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
179
183
 
180
184
  const otelMetricsAdapter = new OtelMetricsAdapter(telemetry);
181
185
 
186
+ // If bootstrap nodes are provided, also provide them to the p2p service
187
+ const peerDiscovery = [];
188
+ if (peerDiscoveryService.bootstrapNodes.length > 0) {
189
+ peerDiscovery.push(bootstrap({ list: peerDiscoveryService.bootstrapNodes }));
190
+ }
191
+
182
192
  const node = await createLibp2p({
183
193
  start: false,
184
194
  peerId,
@@ -200,24 +210,35 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
200
210
  }),
201
211
  ],
202
212
  datastore,
203
- streamMuxers: [yamux(), mplex()],
213
+ peerDiscovery,
214
+ streamMuxers: [mplex(), yamux()],
204
215
  connectionEncryption: [noise()],
205
216
  connectionManager: {
206
- minConnections: minPeerCount,
217
+ minConnections: 0,
207
218
  maxConnections: maxPeerCount,
219
+
220
+ maxParallelDials: 100,
221
+ maxPeerAddrsToDial: 5,
222
+ maxIncomingPendingConnections: 5,
208
223
  },
209
224
  services: {
210
225
  identify: identify({
211
226
  protocolPrefix: 'aztec',
212
227
  }),
213
228
  pubsub: gossipsub({
229
+ debugName: 'gossipsub',
230
+ globalSignaturePolicy: SignaturePolicy.StrictNoSign,
214
231
  allowPublishToZeroTopicPeers: true,
232
+ floodPublish: config.gossipsubFloodPublish,
215
233
  D: config.gossipsubD,
216
234
  Dlo: config.gossipsubDlo,
217
235
  Dhi: config.gossipsubDhi,
236
+ Dlazy: config.gossipsubDLazy,
218
237
  heartbeatInterval: config.gossipsubInterval,
219
238
  mcacheLength: config.gossipsubMcacheLength,
220
239
  mcacheGossip: config.gossipsubMcacheGossip,
240
+ // Increased from default 3s to give time for input lag: configuration and rationale from lodestar
241
+ gossipsubIWantFollowupMs: 12 * 1000,
221
242
  msgIdFn: getMsgIdFn,
222
243
  msgIdToStrFn: msgIdToStrFn,
223
244
  fastMsgIdFn: fastMsgIdFn,
@@ -226,6 +247,8 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
226
247
  metricsTopicStrToLabel: metricsTopicStrToLabels(),
227
248
  asyncValidation: true,
228
249
  scoreParams: createPeerScoreParams({
250
+ // IPColocation factor can be disabled for local testing - default to -5
251
+ IPColocationFactorWeight: config.debugDisableColocationPenalty ? 0 : -5.0,
229
252
  topics: {
230
253
  [Tx.p2pTopic]: createTopicScoreParams({
231
254
  topicWeight: 1,
@@ -254,6 +277,8 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
254
277
  connectionManager: components.connectionManager,
255
278
  }),
256
279
  },
280
+ // Fix the peer id in libp2p logs so we can see the source of the log
281
+ logger: createLibp2pComponentLogger(logger.module, { sourcePeerId: peerId }),
257
282
  });
258
283
 
259
284
  return new LibP2PService(
@@ -267,6 +292,7 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
267
292
  proofVerifier,
268
293
  worldStateSynchronizer,
269
294
  telemetry,
295
+ logger,
270
296
  );
271
297
  }
272
298
 
@@ -319,8 +345,16 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
319
345
  [BlockProposal.p2pTopic]: this.validatePropagatedBlockFromMessage.bind(this),
320
346
  [EpochProofQuote.p2pTopic]: this.validatePropagatedEpochProofQuoteFromMessage.bind(this),
321
347
  };
322
- for (const [topic, validator] of Object.entries(topicValidators)) {
323
- this.node.services.pubsub.topicValidators.set(topic, validator);
348
+ // When running bandwidth benchmarks, we use send blobs of data we do not want to validate
349
+ // NEVER switch this off in production
350
+ if (!this.config.debugDisableMessageValidation) {
351
+ for (const [topic, validator] of Object.entries(topicValidators)) {
352
+ this.node.services.pubsub.topicValidators.set(topic, validator);
353
+ }
354
+ } else {
355
+ this.logger.warn(
356
+ 'MESSAGE VALIDATION DISABLED - IF YOU SEE THIS LOG AND ARE NOT DEBUGGING AND ARE RUNNING IN A PRODUCTION ENVIRONMENT, PLEASE RE-ENABLE MESSAGE VALIDATION',
357
+ );
324
358
  }
325
359
 
326
360
  // add GossipSub listener
@@ -904,7 +938,10 @@ export class LibP2PService<T extends P2PClientType> extends WithTracer implement
904
938
  this.logger.trace(`Sending message ${identifier}`, { p2pMessageIdentifier: identifier });
905
939
 
906
940
  const recipientsNum = await this.publishToTopic(parent.p2pTopic, message.toBuffer());
907
- this.logger.debug(`Sent message ${identifier} to ${recipientsNum} peers`, { p2pMessageIdentifier: identifier });
941
+ this.logger.debug(`Sent message ${identifier} to ${recipientsNum} peers`, {
942
+ p2pMessageIdentifier: identifier,
943
+ sourcePeer: this.node.peerId.toString(),
944
+ });
908
945
  }
909
946
 
910
947
  // Libp2p seems to hang sometimes if new peers are initiating connections.
@@ -3,6 +3,8 @@ import { Fr } from '@aztec/foundation/fields';
3
3
 
4
4
  import { type PeerId } from '@libp2p/interface';
5
5
 
6
+ import { type ReqRespStatus } from './status.js';
7
+
6
8
  /*
7
9
  * Request Response Sub Protocols
8
10
  */
@@ -31,6 +33,15 @@ export type ReqRespSubProtocolHandler = (peerId: PeerId, msg: Buffer) => Promise
31
33
  */
32
34
  export type ReqRespSubProtocolRateLimits = Record<ReqRespSubProtocol, ProtocolRateLimitQuota>;
33
35
 
36
+ /**
37
+ * The response from the ReqResp protocol
38
+ * Consists of a status (Error code) and data
39
+ */
40
+ export interface ReqRespResponse {
41
+ status: ReqRespStatus;
42
+ data: Buffer;
43
+ }
44
+
34
45
  /**
35
46
  * A rate limit quota
36
47
  */
@@ -95,7 +95,7 @@ export function reqGoodbyeHandler(peerManager: PeerManager): ReqRespSubProtocolH
95
95
 
96
96
  peerManager.goodbyeReceived(peerId, reason);
97
97
 
98
- // Return a buffer of length 1 as an acknowledgement
98
+ // Return a buffer of length 1 as an acknowledgement: this is allowed to fail
99
99
  return Promise.resolve(Buffer.from([0x0]));
100
100
  };
101
101
  }