@aztec/p2p 0.65.2 → 0.67.0

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 (87) hide show
  1. package/dest/bootstrap/bootstrap.d.ts +7 -2
  2. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  3. package/dest/bootstrap/bootstrap.js +25 -12
  4. package/dest/client/index.d.ts.map +1 -1
  5. package/dest/client/index.js +11 -7
  6. package/dest/client/p2p_client.d.ts +11 -16
  7. package/dest/client/p2p_client.d.ts.map +1 -1
  8. package/dest/client/p2p_client.js +32 -8
  9. package/dest/config.d.ts +40 -2
  10. package/dest/config.d.ts.map +1 -1
  11. package/dest/config.js +10 -2
  12. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +8 -0
  13. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  14. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +1 -0
  15. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
  16. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +22 -3
  17. package/dest/mem_pools/instrumentation.d.ts +2 -7
  18. package/dest/mem_pools/instrumentation.d.ts.map +1 -1
  19. package/dest/mem_pools/instrumentation.js +4 -25
  20. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
  21. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +5 -7
  22. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
  23. package/dest/mem_pools/tx_pool/memory_tx_pool.js +4 -4
  24. package/dest/mocks/index.d.ts.map +1 -1
  25. package/dest/mocks/index.js +12 -6
  26. package/dest/service/discV5_service.d.ts +2 -0
  27. package/dest/service/discV5_service.d.ts.map +1 -1
  28. package/dest/service/discV5_service.js +14 -11
  29. package/dest/service/dummy_service.d.ts +3 -1
  30. package/dest/service/dummy_service.d.ts.map +1 -1
  31. package/dest/service/dummy_service.js +5 -1
  32. package/dest/service/encoding.d.ts +26 -0
  33. package/dest/service/encoding.d.ts.map +1 -0
  34. package/dest/service/encoding.js +49 -0
  35. package/dest/service/libp2p_service.d.ts +2 -7
  36. package/dest/service/libp2p_service.d.ts.map +1 -1
  37. package/dest/service/libp2p_service.js +24 -31
  38. package/dest/service/peer_manager.d.ts +3 -0
  39. package/dest/service/peer_manager.d.ts.map +1 -1
  40. package/dest/service/peer_manager.js +59 -21
  41. package/dest/service/peer_scoring.d.ts +4 -1
  42. package/dest/service/peer_scoring.d.ts.map +1 -1
  43. package/dest/service/peer_scoring.js +6 -1
  44. package/dest/service/reqresp/handlers.d.ts +4 -2
  45. package/dest/service/reqresp/handlers.d.ts.map +1 -1
  46. package/dest/service/reqresp/handlers.js +3 -3
  47. package/dest/service/reqresp/interface.d.ts +1 -1
  48. package/dest/service/reqresp/interface.d.ts.map +1 -1
  49. package/dest/service/reqresp/interface.js +2 -2
  50. package/dest/service/reqresp/reqresp.d.ts +3 -0
  51. package/dest/service/reqresp/reqresp.d.ts.map +1 -1
  52. package/dest/service/reqresp/reqresp.js +14 -9
  53. package/dest/service/service.d.ts +2 -1
  54. package/dest/service/service.d.ts.map +1 -1
  55. package/dest/tx_validator/data_validator.js +3 -3
  56. package/dest/tx_validator/double_spend_validator.js +3 -3
  57. package/dest/tx_validator/metadata_validator.js +3 -3
  58. package/dest/tx_validator/tx_proof_validator.js +3 -3
  59. package/dest/util.d.ts +20 -2
  60. package/dest/util.d.ts.map +1 -1
  61. package/dest/util.js +39 -3
  62. package/package.json +14 -9
  63. package/src/bootstrap/bootstrap.ts +33 -13
  64. package/src/client/index.ts +10 -6
  65. package/src/client/p2p_client.ts +47 -24
  66. package/src/config.ts +17 -2
  67. package/src/mem_pools/attestation_pool/attestation_pool.ts +9 -0
  68. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +23 -2
  69. package/src/mem_pools/instrumentation.ts +4 -26
  70. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +4 -6
  71. package/src/mem_pools/tx_pool/memory_tx_pool.ts +3 -3
  72. package/src/mocks/index.ts +11 -5
  73. package/src/service/discV5_service.ts +17 -12
  74. package/src/service/dummy_service.ts +6 -1
  75. package/src/service/encoding.ts +61 -0
  76. package/src/service/libp2p_service.ts +26 -32
  77. package/src/service/peer_manager.ts +68 -22
  78. package/src/service/peer_scoring.ts +8 -1
  79. package/src/service/reqresp/handlers.ts +4 -4
  80. package/src/service/reqresp/interface.ts +3 -3
  81. package/src/service/reqresp/reqresp.ts +13 -8
  82. package/src/service/service.ts +3 -1
  83. package/src/tx_validator/data_validator.ts +2 -2
  84. package/src/tx_validator/double_spend_validator.ts +2 -2
  85. package/src/tx_validator/metadata_validator.ts +2 -2
  86. package/src/tx_validator/tx_proof_validator.ts +2 -2
  87. package/src/util.ts +48 -2
@@ -1,5 +1,5 @@
1
1
  import { type BlockAttestation } from '@aztec/circuit-types';
2
- import { createDebugLogger } from '@aztec/foundation/log';
2
+ import { createLogger } from '@aztec/foundation/log';
3
3
  import { type TelemetryClient } from '@aztec/telemetry-client';
4
4
 
5
5
  import { PoolInstrumentation, PoolName } from '../instrumentation.js';
@@ -10,7 +10,7 @@ export class InMemoryAttestationPool implements AttestationPool {
10
10
 
11
11
  private attestations: Map</*slot=*/ bigint, Map</*proposalId*/ string, Map</*address=*/ string, BlockAttestation>>>;
12
12
 
13
- constructor(telemetry: TelemetryClient, private log = createDebugLogger('aztec:attestation_pool')) {
13
+ constructor(telemetry: TelemetryClient, private log = createLogger('p2p:attestation_pool')) {
14
14
  this.attestations = new Map();
15
15
  this.metrics = new PoolInstrumentation(telemetry, PoolName.ATTESTATION_POOL);
16
16
  }
@@ -58,6 +58,27 @@ export class InMemoryAttestationPool implements AttestationPool {
58
58
  return total;
59
59
  }
60
60
 
61
+ public async deleteAttestationsOlderThan(oldestSlot: bigint): Promise<void> {
62
+ const olderThan = [];
63
+
64
+ // Entries are iterated in insertion order, so we can break as soon as we find a slot that is older than the oldestSlot.
65
+ // Note: this will only prune correctly if attestations are added in order of rising slot, it is important that we do not allow
66
+ // insertion of attestations that are old. #(https://github.com/AztecProtocol/aztec-packages/issues/10322)
67
+ const slots = this.attestations.keys();
68
+ for (const slot of slots) {
69
+ if (slot < oldestSlot) {
70
+ olderThan.push(slot);
71
+ } else {
72
+ break;
73
+ }
74
+ }
75
+
76
+ for (const oldSlot of olderThan) {
77
+ await this.deleteAttestationsForSlot(oldSlot);
78
+ }
79
+ return Promise.resolve();
80
+ }
81
+
61
82
  public deleteAttestationsForSlot(slot: bigint): Promise<void> {
62
83
  // We count the number of attestations we are removing
63
84
  const numberOfAttestations = this.#getNumberOfAttestationsInSlot(slot);
@@ -3,6 +3,7 @@ import {
3
3
  Attributes,
4
4
  type Histogram,
5
5
  LmdbMetrics,
6
+ type LmdbStatsCallback,
6
7
  Metrics,
7
8
  type TelemetryClient,
8
9
  type UpDownCounter,
@@ -58,7 +59,7 @@ export class PoolInstrumentation<PoolObject extends Gossipable> {
58
59
 
59
60
  private defaultAttributes;
60
61
 
61
- constructor(telemetry: TelemetryClient, name: PoolName) {
62
+ constructor(telemetry: TelemetryClient, name: PoolName, dbStats?: LmdbStatsCallback) {
62
63
  const meter = telemetry.getMeter(name);
63
64
  this.defaultAttributes = { [Attributes.POOL_NAME]: name };
64
65
 
@@ -71,40 +72,17 @@ export class PoolInstrumentation<PoolObject extends Gossipable> {
71
72
  this.objectSize = meter.createHistogram(metricsLabels.objectSize, {
72
73
  unit: 'By',
73
74
  description: 'The size of transactions in the mempool',
74
- advice: {
75
- explicitBucketBoundaries: [
76
- 5_000, // 5KB
77
- 10_000,
78
- 20_000,
79
- 50_000,
80
- 75_000,
81
- 100_000, // 100KB
82
- 200_000,
83
- ],
84
- },
85
75
  });
86
76
 
87
77
  this.dbMetrics = new LmdbMetrics(
88
78
  meter,
89
79
  {
90
- name: Metrics.MEMPOOL_DB_MAP_SIZE,
91
- description: 'Database map size for the Tx mempool',
92
- },
93
- {
94
- name: Metrics.MEMPOOL_DB_USED_SIZE,
95
- description: 'Database used size for the Tx mempool',
96
- },
97
- {
98
- name: Metrics.MEMPOOL_DB_NUM_ITEMS,
99
- description: 'Num items in database for the Tx mempool',
80
+ [Attributes.DB_DATA_TYPE]: 'tx-pool',
100
81
  },
82
+ dbStats,
101
83
  );
102
84
  }
103
85
 
104
- public recordDBMetrics(metrics: { mappingSize: number; numItems: number; actualSize: number }) {
105
- this.dbMetrics.recordDBMetrics(metrics);
106
- }
107
-
108
86
  public recordSize(poolObject: PoolObject) {
109
87
  this.objectSize.record(poolObject.getSize());
110
88
  }
@@ -1,6 +1,6 @@
1
1
  import { Tx, TxHash } from '@aztec/circuit-types';
2
2
  import { type TxAddedToPoolStats } from '@aztec/circuit-types/stats';
3
- import { type Logger, createDebugLogger } from '@aztec/foundation/log';
3
+ import { type Logger, createLogger } from '@aztec/foundation/log';
4
4
  import { type AztecKVStore, type AztecMap, type AztecSet } from '@aztec/kv-store';
5
5
  import { type TelemetryClient } from '@aztec/telemetry-client';
6
6
 
@@ -30,14 +30,14 @@ export class AztecKVTxPool implements TxPool {
30
30
  * @param store - A KV store.
31
31
  * @param log - A logger.
32
32
  */
33
- constructor(store: AztecKVStore, telemetry: TelemetryClient, log = createDebugLogger('aztec:tx_pool')) {
33
+ constructor(store: AztecKVStore, telemetry: TelemetryClient, log = createLogger('p2p:tx_pool')) {
34
34
  this.#txs = store.openMap('txs');
35
35
  this.#minedTxs = store.openMap('minedTxs');
36
36
  this.#pendingTxs = store.openSet('pendingTxs');
37
37
 
38
38
  this.#store = store;
39
39
  this.#log = log;
40
- this.#metrics = new PoolInstrumentation(telemetry, PoolName.TX_POOL);
40
+ this.#metrics = new PoolInstrumentation(telemetry, PoolName.TX_POOL, () => store.estimateSize());
41
41
  }
42
42
 
43
43
  public markAsMined(txHashes: TxHash[], blockNumber: number): Promise<void> {
@@ -53,8 +53,6 @@ export class AztecKVTxPool implements TxPool {
53
53
  }
54
54
  this.#metrics.recordRemovedObjects(deleted, 'pending');
55
55
  this.#metrics.recordAddedObjects(txHashes.length, 'mined');
56
- const storeSizes = this.#store.estimateSize();
57
- this.#metrics.recordDBMetrics(storeSizes);
58
56
  });
59
57
  }
60
58
 
@@ -127,7 +125,7 @@ export class AztecKVTxPool implements TxPool {
127
125
  let pendingCount = 0;
128
126
  for (const [i, tx] of txs.entries()) {
129
127
  const txHash = txHashes[i];
130
- this.#log.info(`Adding tx with id ${txHash.toString()}`, {
128
+ this.#log.verbose(`Adding tx ${txHash.toString()} to pool`, {
131
129
  eventName: 'tx-added-to-pool',
132
130
  ...tx.getStats(),
133
131
  } satisfies TxAddedToPoolStats);
@@ -1,6 +1,6 @@
1
1
  import { Tx, TxHash } from '@aztec/circuit-types';
2
2
  import { type TxAddedToPoolStats } from '@aztec/circuit-types/stats';
3
- import { createDebugLogger } from '@aztec/foundation/log';
3
+ import { createLogger } from '@aztec/foundation/log';
4
4
  import { type TelemetryClient } from '@aztec/telemetry-client';
5
5
 
6
6
  import { PoolInstrumentation, PoolName } from '../instrumentation.js';
@@ -23,7 +23,7 @@ export class InMemoryTxPool implements TxPool {
23
23
  * Class constructor for in-memory TxPool. Initiates our transaction pool as a JS Map.
24
24
  * @param log - A logger.
25
25
  */
26
- constructor(telemetry: TelemetryClient, private log = createDebugLogger('aztec:tx_pool')) {
26
+ constructor(telemetry: TelemetryClient, private log = createLogger('p2p:tx_pool')) {
27
27
  this.txs = new Map<bigint, Tx>();
28
28
  this.minedTxs = new Map();
29
29
  this.pendingTxs = new Set();
@@ -105,7 +105,7 @@ export class InMemoryTxPool implements TxPool {
105
105
  let pending = 0;
106
106
  for (const tx of txs) {
107
107
  const txHash = tx.getTxHash();
108
- this.log.debug(`Adding tx with id ${txHash.toString()}`, {
108
+ this.log.verbose(`Adding tx ${txHash.toString()} to pool`, {
109
109
  eventName: 'tx-added-to-pool',
110
110
  ...tx.getStats(),
111
111
  } satisfies TxAddedToPoolStats);
@@ -5,6 +5,7 @@ import {
5
5
  type WorldStateSynchronizer,
6
6
  } from '@aztec/circuit-types';
7
7
  import { type DataStoreConfig } from '@aztec/kv-store/config';
8
+ import { openTmpStore } from '@aztec/kv-store/lmdb';
8
9
  import { type TelemetryClient } from '@aztec/telemetry-client';
9
10
  import { NoopTelemetryClient } from '@aztec/telemetry-client/noop';
10
11
 
@@ -14,6 +15,7 @@ import { yamux } from '@chainsafe/libp2p-yamux';
14
15
  import { bootstrap } from '@libp2p/bootstrap';
15
16
  import { identify } from '@libp2p/identify';
16
17
  import { type PeerId } from '@libp2p/interface';
18
+ import { createSecp256k1PeerId } from '@libp2p/peer-id-factory';
17
19
  import { tcp } from '@libp2p/tcp';
18
20
  import getPort from 'get-port';
19
21
  import { type Libp2p, type Libp2pOptions, createLibp2p } from 'libp2p';
@@ -22,7 +24,7 @@ import { BootstrapNode } from '../bootstrap/bootstrap.js';
22
24
  import { type BootnodeConfig, type P2PConfig } from '../config.js';
23
25
  import { type MemPools } from '../mem_pools/interface.js';
24
26
  import { DiscV5Service } from '../service/discV5_service.js';
25
- import { LibP2PService, createLibP2PPeerId } from '../service/libp2p_service.js';
27
+ import { LibP2PService } from '../service/libp2p_service.js';
26
28
  import { type PeerManager } from '../service/peer_manager.js';
27
29
  import { type P2PReqRespConfig } from '../service/reqresp/config.js';
28
30
  import { pingHandler, statusHandler } from '../service/reqresp/handlers.js';
@@ -102,7 +104,7 @@ export async function createTestLibP2PService(
102
104
  port: number = 0,
103
105
  peerId?: PeerId,
104
106
  ) {
105
- peerId = peerId ?? (await createLibP2PPeerId());
107
+ peerId = peerId ?? (await createSecp256k1PeerId());
106
108
  const config = {
107
109
  tcpAnnounceAddress: `127.0.0.1:${port}`,
108
110
  udpAnnounceAddress: `127.0.0.1:${port}`,
@@ -146,7 +148,7 @@ export type ReqRespNode = {
146
148
  export const MOCK_SUB_PROTOCOL_HANDLERS: ReqRespSubProtocolHandlers = {
147
149
  [PING_PROTOCOL]: pingHandler,
148
150
  [STATUS_PROTOCOL]: statusHandler,
149
- [TX_REQ_PROTOCOL]: (_msg: any) => Promise.resolve(Uint8Array.from(Buffer.from('tx'))),
151
+ [TX_REQ_PROTOCOL]: (_msg: any) => Promise.resolve(Buffer.from('tx')),
150
152
  };
151
153
 
152
154
  // By default, all requests are valid
@@ -231,6 +233,8 @@ export function createBootstrapNodeConfig(privateKey: string, port: number): Boo
231
233
  peerIdPrivateKey: privateKey,
232
234
  minPeerCount: 10,
233
235
  maxPeerCount: 100,
236
+ dataDirectory: undefined,
237
+ dataStoreMapSizeKB: 0,
234
238
  };
235
239
  }
236
240
 
@@ -247,14 +251,16 @@ export async function createBootstrapNode(
247
251
  port: number,
248
252
  telemetry: TelemetryClient = new NoopTelemetryClient(),
249
253
  ): Promise<BootstrapNode> {
250
- const peerId = await createLibP2PPeerId();
254
+ const peerId = await createSecp256k1PeerId();
251
255
  const config = createBootstrapNodeConfig(Buffer.from(peerId.privateKey!).toString('hex'), port);
252
256
 
253
257
  return startBootstrapNode(config, telemetry);
254
258
  }
255
259
 
256
260
  async function startBootstrapNode(config: BootnodeConfig, telemetry: TelemetryClient) {
257
- const bootstrapNode = new BootstrapNode(telemetry);
261
+ // Open an ephemeral store that will only exist in memory
262
+ const store = openTmpStore(true);
263
+ const bootstrapNode = new BootstrapNode(store, telemetry);
258
264
  await bootstrapNode.start(config);
259
265
  return bootstrapNode;
260
266
  }
@@ -1,11 +1,11 @@
1
- import { createDebugLogger } from '@aztec/foundation/log';
1
+ import { createLogger } from '@aztec/foundation/log';
2
2
  import { sleep } from '@aztec/foundation/sleep';
3
3
  import { OtelMetricsAdapter, type TelemetryClient } from '@aztec/telemetry-client';
4
4
 
5
5
  import { Discv5, type Discv5EventEmitter } from '@chainsafe/discv5';
6
6
  import { ENR, SignableENR } from '@chainsafe/enr';
7
7
  import type { PeerId } from '@libp2p/interface';
8
- import { multiaddr } from '@multiformats/multiaddr';
8
+ import { type Multiaddr, multiaddr } from '@multiformats/multiaddr';
9
9
  import EventEmitter from 'events';
10
10
 
11
11
  import type { P2PConfig } from '../config.js';
@@ -35,6 +35,9 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
35
35
  /** This instance's ENR */
36
36
  private enr: SignableENR;
37
37
 
38
+ /** UDP listen addr */
39
+ private listenMultiAddrUdp: Multiaddr;
40
+
38
41
  private currentState = PeerDiscoveryState.STOPPED;
39
42
 
40
43
  private bootstrapNodes: string[];
@@ -46,7 +49,7 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
46
49
  private peerId: PeerId,
47
50
  config: P2PConfig,
48
51
  telemetry: TelemetryClient,
49
- private logger = createDebugLogger('aztec:discv5_service'),
52
+ private logger = createLogger('p2p:discv5_service'),
50
53
  ) {
51
54
  super();
52
55
  const { tcpAnnounceAddress, udpAnnounceAddress, udpListenAddress, bootstrapNodes } = config;
@@ -66,7 +69,7 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
66
69
  `${convertToMultiaddr(udpAnnounceAddress || tcpAnnounceAddress, 'udp')}/p2p/${peerId.toString()}`,
67
70
  );
68
71
 
69
- const listenMultiAddrUdp = multiaddr(convertToMultiaddr(udpListenAddress, 'udp'));
72
+ this.listenMultiAddrUdp = multiaddr(convertToMultiaddr(udpListenAddress, 'udp'));
70
73
 
71
74
  // set location multiaddr in ENR record
72
75
  this.enr.setLocationMultiaddr(multiAddrUdp);
@@ -76,7 +79,7 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
76
79
  this.discv5 = Discv5.create({
77
80
  enr: this.enr,
78
81
  peerId,
79
- bindAddrs: { ip4: listenMultiAddrUdp },
82
+ bindAddrs: { ip4: this.listenMultiAddrUdp },
80
83
  config: {
81
84
  lookupTimeout: 2000,
82
85
  requestTimeout: 2000,
@@ -85,14 +88,11 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
85
88
  metricsRegistry,
86
89
  });
87
90
 
88
- this.logger.info(`ENR NodeId: ${this.enr.nodeId}`);
89
- this.logger.info(`ENR UDP: ${multiAddrUdp.toString()}`);
90
-
91
91
  (this.discv5 as Discv5EventEmitter).on('discovered', (enr: ENR) => this.onDiscovered(enr));
92
92
  (this.discv5 as Discv5EventEmitter).on('enrAdded', async (enr: ENR) => {
93
93
  const multiAddrTcp = await enr.getFullMultiaddr('tcp');
94
94
  const multiAddrUdp = await enr.getFullMultiaddr('udp');
95
- this.logger.debug(`ENR multiaddr: ${multiAddrTcp?.toString()}, ${multiAddrUdp?.toString()}`);
95
+ this.logger.debug(`Added ENR ${enr.encodeTxt()}`, { multiAddrTcp, multiAddrUdp, nodeId: enr.nodeId });
96
96
  this.onDiscovered(enr);
97
97
  });
98
98
  }
@@ -101,18 +101,23 @@ export class DiscV5Service extends EventEmitter implements PeerDiscoveryService
101
101
  if (this.currentState === PeerDiscoveryState.RUNNING) {
102
102
  throw new Error('DiscV5Service already started');
103
103
  }
104
- this.logger.info('Starting DiscV5');
104
+ this.logger.debug('Starting DiscV5');
105
105
  await this.discv5.start();
106
106
  this.startTime = Date.now();
107
107
 
108
- this.logger.info('DiscV5 started');
108
+ this.logger.info(`DiscV5 service started`, {
109
+ nodeId: this.enr.nodeId,
110
+ peerId: this.peerId,
111
+ enrUdp: await this.enr.getFullMultiaddr('udp'),
112
+ enrTcp: await this.enr.getFullMultiaddr('tcp'),
113
+ });
109
114
  this.currentState = PeerDiscoveryState.RUNNING;
110
115
 
111
116
  // Add bootnode ENR if provided
112
117
  if (this.bootstrapNodes?.length) {
113
118
  // Do this conversion once since it involves an async function call
114
119
  this.bootstrapNodePeerIds = await Promise.all(this.bootstrapNodes.map(enr => ENR.decodeTxt(enr).peerId()));
115
- this.logger.info(`Adding bootstrap ENRs: ${this.bootstrapNodes.join(', ')}`);
120
+ this.logger.info(`Adding bootstrap nodes ENRs: ${this.bootstrapNodes.join(', ')}`);
116
121
  try {
117
122
  this.bootstrapNodes.forEach(enr => {
118
123
  this.discv5.addEnr(enr);
@@ -1,4 +1,4 @@
1
- import type { BlockAttestation, BlockProposal, Gossipable, TxHash } from '@aztec/circuit-types';
1
+ import type { BlockAttestation, BlockProposal, Gossipable, PeerInfo, TxHash } from '@aztec/circuit-types';
2
2
 
3
3
  import type { PeerId } from '@libp2p/interface';
4
4
  import EventEmitter from 'events';
@@ -10,6 +10,11 @@ import { type P2PService, type PeerDiscoveryService, PeerDiscoveryState } from '
10
10
  * A dummy implementation of the P2P Service.
11
11
  */
12
12
  export class DummyP2PService implements P2PService {
13
+ /** Returns an empty array for peers. */
14
+ getPeers(): PeerInfo[] {
15
+ return [];
16
+ }
17
+
13
18
  /**
14
19
  * Starts the dummy implementation.
15
20
  * @returns A resolved promise.
@@ -0,0 +1,61 @@
1
+ // Taken from lodestar: https://github.com/ChainSafe/lodestar
2
+ import { sha256 } from '@aztec/foundation/crypto';
3
+
4
+ import { type RPC } from '@chainsafe/libp2p-gossipsub/message';
5
+ import { type DataTransform } from '@chainsafe/libp2p-gossipsub/types';
6
+ import { type Message } from '@libp2p/interface';
7
+ import { compressSync, uncompressSync } from 'snappy';
8
+ import xxhashFactory from 'xxhash-wasm';
9
+
10
+ // Load WASM
11
+ const xxhash = await xxhashFactory();
12
+
13
+ // Use salt to prevent msgId from being mined for collisions
14
+ const h64Seed = BigInt(Math.floor(Math.random() * 1e9));
15
+
16
+ // Shared buffer to convert msgId to string
17
+ const sharedMsgIdBuf = Buffer.alloc(20);
18
+
19
+ /**
20
+ * The function used to generate a gossipsub message id
21
+ * We use the first 8 bytes of SHA256(data) for content addressing
22
+ */
23
+ export function fastMsgIdFn(rpcMsg: RPC.Message): string {
24
+ if (rpcMsg.data) {
25
+ return xxhash.h64Raw(rpcMsg.data, h64Seed).toString(16);
26
+ }
27
+ return '0000000000000000';
28
+ }
29
+
30
+ export function msgIdToStrFn(msgId: Uint8Array): string {
31
+ // This happens serially, no need to reallocate the buffer
32
+ sharedMsgIdBuf.set(msgId);
33
+ return `0x${sharedMsgIdBuf.toString('hex')}`;
34
+ }
35
+
36
+ /**
37
+ * Get the message identifier from a libp2p message
38
+ *
39
+ * Follows similarly to:
40
+ * https://github.com/ethereum/consensus-specs/blob/v1.1.0-alpha.7/specs/altair/p2p-interface.md#topics-and-messages
41
+ *
42
+ * @param message - The libp2p message
43
+ * @returns The message identifier
44
+ */
45
+ export function getMsgIdFn(message: Message) {
46
+ const { topic } = message;
47
+
48
+ const vec = [Buffer.from(topic), message.data];
49
+ return sha256(Buffer.concat(vec)).subarray(0, 20);
50
+ }
51
+
52
+ export class SnappyTransform implements DataTransform {
53
+ inboundTransform(_topicStr: string, data: Uint8Array): Uint8Array {
54
+ const uncompressed = Buffer.from(uncompressSync(Buffer.from(data), { asBuffer: true }));
55
+ return new Uint8Array(uncompressed);
56
+ }
57
+
58
+ outboundTransform(_topicStr: string, data: Uint8Array): Uint8Array {
59
+ return new Uint8Array(compressSync(Buffer.from(data)));
60
+ }
61
+ }
@@ -6,6 +6,7 @@ import {
6
6
  type Gossipable,
7
7
  type L2BlockSource,
8
8
  MerkleTreeId,
9
+ type PeerInfo,
9
10
  type RawGossipMessage,
10
11
  TopicType,
11
12
  TopicTypeMap,
@@ -15,7 +16,7 @@ import {
15
16
  metricsTopicStrToLabels,
16
17
  } from '@aztec/circuit-types';
17
18
  import { Fr } from '@aztec/circuits.js';
18
- import { createDebugLogger } from '@aztec/foundation/log';
19
+ import { createLogger } from '@aztec/foundation/log';
19
20
  import { SerialQueue } from '@aztec/foundation/queue';
20
21
  import { RunningPromise } from '@aztec/foundation/running-promise';
21
22
  import type { AztecKVStore } from '@aztec/kv-store';
@@ -30,7 +31,6 @@ import { identify } from '@libp2p/identify';
30
31
  import type { PeerId } from '@libp2p/interface';
31
32
  import '@libp2p/kad-dht';
32
33
  import { mplex } from '@libp2p/mplex';
33
- import { createFromJSON, createSecp256k1PeerId } from '@libp2p/peer-id-factory';
34
34
  import { tcp } from '@libp2p/tcp';
35
35
  import { createLibp2p } from 'libp2p';
36
36
 
@@ -44,6 +44,7 @@ import {
44
44
  } from '../tx_validator/index.js';
45
45
  import { type PubSubLibp2p, convertToMultiaddr } from '../util.js';
46
46
  import { AztecDatastore } from './data_store.js';
47
+ import { SnappyTransform, fastMsgIdFn, getMsgIdFn, msgIdToStrFn } from './encoding.js';
47
48
  import { PeerManager } from './peer_manager.js';
48
49
  import { PeerErrorSeverity } from './peer_scoring.js';
49
50
  import { pingHandler, statusHandler } from './reqresp/handlers.js';
@@ -60,22 +61,6 @@ import {
60
61
  import { ReqResp } from './reqresp/reqresp.js';
61
62
  import type { P2PService, PeerDiscoveryService } from './service.js';
62
63
 
63
- /**
64
- * Create a libp2p peer ID from the private key if provided, otherwise creates a new random ID.
65
- * @param privateKey - Optional peer ID private key as hex string
66
- * @returns The peer ID.
67
- */
68
- export async function createLibP2PPeerId(privateKey?: string): Promise<PeerId> {
69
- if (!privateKey?.length) {
70
- return await createSecp256k1PeerId();
71
- }
72
- const base64 = Buffer.from(privateKey, 'hex').toString('base64');
73
- return await createFromJSON({
74
- id: '',
75
- privKey: base64,
76
- });
77
- }
78
-
79
64
  /**
80
65
  * Lib P2P implementation of the P2PService interface.
81
66
  */
@@ -104,7 +89,7 @@ export class LibP2PService extends WithTracer implements P2PService {
104
89
  private worldStateSynchronizer: WorldStateSynchronizer,
105
90
  private telemetry: TelemetryClient,
106
91
  private requestResponseHandlers: ReqRespSubProtocolHandlers = DEFAULT_SUB_PROTOCOL_HANDLERS,
107
- private logger = createDebugLogger('aztec:libp2p_service'),
92
+ private logger = createLogger('p2p:libp2p_service'),
108
93
  ) {
109
94
  super(telemetry, 'LibP2PService');
110
95
 
@@ -133,20 +118,17 @@ export class LibP2PService extends WithTracer implements P2PService {
133
118
  throw new Error('P2P service already started');
134
119
  }
135
120
 
136
- // Log listen & announce addresses
121
+ // Get listen & announce addresses for logging
137
122
  const { tcpListenAddress, tcpAnnounceAddress } = this.config;
138
- this.logger.info(`Starting P2P node on ${tcpListenAddress}`);
139
123
  if (!tcpAnnounceAddress) {
140
124
  throw new Error('Announce address not provided.');
141
125
  }
142
126
  const announceTcpMultiaddr = convertToMultiaddr(tcpAnnounceAddress, 'tcp');
143
- this.logger.info(`Announcing at ${announceTcpMultiaddr}`);
144
127
 
145
128
  // Start job queue, peer discovery service and libp2p node
146
129
  this.jobQueue.start();
147
130
  await this.peerDiscoveryService.start();
148
131
  await this.node.start();
149
- this.logger.info(`Started P2P client with Peer ID ${this.node.peerId.toString()}`);
150
132
 
151
133
  // Subscribe to standard GossipSub topics by default
152
134
  for (const topic in TopicType) {
@@ -156,7 +138,7 @@ export class LibP2PService extends WithTracer implements P2PService {
156
138
  // add GossipSub listener
157
139
  this.node.services.pubsub.addEventListener('gossipsub:message', async e => {
158
140
  const { msg, propagationSource: peerId } = e.detail;
159
- this.logger.debug(`Received PUBSUB message.`);
141
+ this.logger.trace(`Received PUBSUB message.`);
160
142
 
161
143
  await this.jobQueue.put(() => this.handleNewGossipMessage(msg, peerId));
162
144
  });
@@ -173,6 +155,11 @@ export class LibP2PService extends WithTracer implements P2PService {
173
155
  [TX_REQ_PROTOCOL]: this.validateRequestedTx.bind(this),
174
156
  };
175
157
  await this.reqresp.start(this.requestResponseHandlers, reqrespSubProtocolValidators);
158
+ this.logger.info(`Started P2P service`, {
159
+ listen: tcpListenAddress,
160
+ announce: announceTcpMultiaddr,
161
+ peerId: this.node.peerId.toString(),
162
+ });
176
163
  }
177
164
 
178
165
  /**
@@ -191,7 +178,6 @@ export class LibP2PService extends WithTracer implements P2PService {
191
178
  this.logger.debug('Stopping LibP2P...');
192
179
  await this.stopLibP2P();
193
180
  this.logger.info('LibP2P service stopped');
194
- this.logger.debug('Stopping request response service...');
195
181
  }
196
182
 
197
183
  /**
@@ -259,6 +245,10 @@ export class LibP2PService extends WithTracer implements P2PService {
259
245
  heartbeatInterval: config.gossipsubInterval,
260
246
  mcacheLength: config.gossipsubMcacheLength,
261
247
  mcacheGossip: config.gossipsubMcacheGossip,
248
+ msgIdFn: getMsgIdFn,
249
+ msgIdToStrFn: msgIdToStrFn,
250
+ fastMsgIdFn: fastMsgIdFn,
251
+ dataTransform: new SnappyTransform(),
262
252
  metricsRegister: otelMetricsAdapter,
263
253
  metricsTopicStrToLabel: metricsTopicStrToLabels(),
264
254
  scoreParams: createPeerScoreParams({
@@ -295,11 +285,11 @@ export class LibP2PService extends WithTracer implements P2PService {
295
285
  * @param msg - the tx request message
296
286
  * @returns the tx response message
297
287
  */
298
- const txHandler = (msg: Buffer): Promise<Uint8Array> => {
288
+ const txHandler = (msg: Buffer): Promise<Buffer> => {
299
289
  const txHash = TxHash.fromBuffer(msg);
300
290
  const foundTx = mempools.txPool.getTxByHash(txHash);
301
- const asUint8Array = Uint8Array.from(foundTx ? foundTx.toBuffer() : Buffer.alloc(0));
302
- return Promise.resolve(asUint8Array);
291
+ const buf = foundTx ? foundTx.toBuffer() : Buffer.alloc(0);
292
+ return Promise.resolve(buf);
303
293
  };
304
294
 
305
295
  const requestResponseHandlers = {
@@ -321,6 +311,10 @@ export class LibP2PService extends WithTracer implements P2PService {
321
311
  );
322
312
  }
323
313
 
314
+ public getPeers(includePending?: boolean): PeerInfo[] {
315
+ return this.peerManager.getPeers(includePending);
316
+ }
317
+
324
318
  /**
325
319
  * Send Request via the ReqResp service
326
320
  * The subprotocol defined will determine the request and response types
@@ -468,7 +462,7 @@ export class LibP2PService extends WithTracer implements P2PService {
468
462
  * @param message - The message to propagate.
469
463
  */
470
464
  public propagate<T extends Gossipable>(message: T): void {
471
- this.logger.debug(`[${message.p2pMessageIdentifier()}] queued`);
465
+ this.logger.trace(`[${message.p2pMessageIdentifier()}] queued`);
472
466
  void this.jobQueue.put(async () => {
473
467
  await this.sendToPeers(message);
474
468
  });
@@ -595,10 +589,10 @@ export class LibP2PService extends WithTracer implements P2PService {
595
589
  const parent = message.constructor as typeof Gossipable;
596
590
 
597
591
  const identifier = message.p2pMessageIdentifier().toString();
598
- this.logger.verbose(`[${identifier}] sending`);
592
+ this.logger.trace(`Sending message ${identifier}`);
599
593
 
600
594
  const recipientsNum = await this.publishToTopic(parent.p2pTopic, message.toBuffer());
601
- this.logger.verbose(`[${identifier}] sent to ${recipientsNum} peers`);
595
+ this.logger.debug(`Sent message ${identifier} to ${recipientsNum} peers`);
602
596
  }
603
597
 
604
598
  // Libp2p seems to hang sometimes if new peers are initiating connections.
@@ -609,7 +603,7 @@ export class LibP2PService extends WithTracer implements P2PService {
609
603
  });
610
604
  try {
611
605
  await Promise.race([this.node.stop(), timeout]);
612
- this.logger.debug('Libp2p stopped');
606
+ this.logger.debug('LibP2P stopped');
613
607
  } catch (error) {
614
608
  this.logger.error('Error during stop or timeout:', error);
615
609
  }