@aztec/p2p 0.66.0 → 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 (60) hide show
  1. package/dest/bootstrap/bootstrap.d.ts +4 -1
  2. package/dest/bootstrap/bootstrap.d.ts.map +1 -1
  3. package/dest/bootstrap/bootstrap.js +20 -8
  4. package/dest/client/index.d.ts.map +1 -1
  5. package/dest/client/index.js +7 -4
  6. package/dest/client/p2p_client.d.ts +9 -16
  7. package/dest/client/p2p_client.d.ts.map +1 -1
  8. package/dest/client/p2p_client.js +23 -7
  9. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
  10. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +3 -3
  11. package/dest/mem_pools/instrumentation.d.ts.map +1 -1
  12. package/dest/mem_pools/instrumentation.js +2 -20
  13. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
  14. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +4 -4
  15. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
  16. package/dest/mem_pools/tx_pool/memory_tx_pool.js +4 -4
  17. package/dest/mocks/index.js +2 -2
  18. package/dest/service/discV5_service.d.ts +2 -0
  19. package/dest/service/discV5_service.d.ts.map +1 -1
  20. package/dest/service/discV5_service.js +14 -11
  21. package/dest/service/dummy_service.d.ts +3 -1
  22. package/dest/service/dummy_service.d.ts.map +1 -1
  23. package/dest/service/dummy_service.js +5 -1
  24. package/dest/service/libp2p_service.d.ts +2 -1
  25. package/dest/service/libp2p_service.d.ts.map +1 -1
  26. package/dest/service/libp2p_service.js +15 -11
  27. package/dest/service/peer_manager.d.ts +3 -0
  28. package/dest/service/peer_manager.d.ts.map +1 -1
  29. package/dest/service/peer_manager.js +59 -21
  30. package/dest/service/peer_scoring.d.ts +4 -1
  31. package/dest/service/peer_scoring.d.ts.map +1 -1
  32. package/dest/service/peer_scoring.js +6 -1
  33. package/dest/service/reqresp/reqresp.d.ts.map +1 -1
  34. package/dest/service/reqresp/reqresp.js +7 -7
  35. package/dest/service/service.d.ts +2 -1
  36. package/dest/service/service.d.ts.map +1 -1
  37. package/dest/tx_validator/data_validator.js +3 -3
  38. package/dest/tx_validator/double_spend_validator.js +3 -3
  39. package/dest/tx_validator/metadata_validator.js +3 -3
  40. package/dest/tx_validator/tx_proof_validator.js +3 -3
  41. package/package.json +10 -7
  42. package/src/bootstrap/bootstrap.ts +23 -9
  43. package/src/client/index.ts +6 -3
  44. package/src/client/p2p_client.ts +32 -23
  45. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +2 -2
  46. package/src/mem_pools/instrumentation.ts +1 -21
  47. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +3 -3
  48. package/src/mem_pools/tx_pool/memory_tx_pool.ts +3 -3
  49. package/src/mocks/index.ts +1 -1
  50. package/src/service/discV5_service.ts +17 -12
  51. package/src/service/dummy_service.ts +6 -1
  52. package/src/service/libp2p_service.ts +16 -10
  53. package/src/service/peer_manager.ts +68 -22
  54. package/src/service/peer_scoring.ts +8 -1
  55. package/src/service/reqresp/reqresp.ts +6 -6
  56. package/src/service/service.ts +3 -1
  57. package/src/tx_validator/data_validator.ts +2 -2
  58. package/src/tx_validator/double_spend_validator.ts +2 -2
  59. package/src/tx_validator/metadata_validator.ts +2 -2
  60. package/src/tx_validator/tx_proof_validator.ts +2 -2
@@ -1,8 +1,10 @@
1
- import { createDebugLogger } from '@aztec/foundation/log';
1
+ import { type PeerInfo } from '@aztec/circuit-types';
2
+ import { createLogger } from '@aztec/foundation/log';
2
3
 
3
4
  import { type ENR } from '@chainsafe/enr';
4
5
  import { type PeerId } from '@libp2p/interface';
5
6
  import { type Multiaddr } from '@multiformats/multiaddr';
7
+ import { inspect } from 'util';
6
8
 
7
9
  import { type P2PConfig } from '../config.js';
8
10
  import { type PubSubLibp2p } from '../util.js';
@@ -22,21 +24,22 @@ type CachedPeer = {
22
24
  export class PeerManager {
23
25
  private cachedPeers: Map<string, CachedPeer> = new Map();
24
26
  private peerScoring: PeerScoring;
27
+ private heartbeatCounter: number = 0;
25
28
 
26
29
  constructor(
27
30
  private libP2PNode: PubSubLibp2p,
28
31
  private peerDiscoveryService: PeerDiscoveryService,
29
32
  private config: P2PConfig,
30
- private logger = createDebugLogger('aztec:p2p:peer_manager'),
33
+ private logger = createLogger('p2p:peer-manager'),
31
34
  ) {
32
35
  this.peerScoring = new PeerScoring(config);
33
36
  // Handle new established connections
34
37
  this.libP2PNode.addEventListener('peer:connect', evt => {
35
38
  const peerId = evt.detail;
36
39
  if (this.peerDiscoveryService.isBootstrapPeer(peerId)) {
37
- this.logger.debug(`Connected to bootstrap peer ${peerId.toString()}`);
40
+ this.logger.verbose(`Connected to bootstrap peer ${peerId.toString()}`);
38
41
  } else {
39
- this.logger.debug(`Connected to transaction peer ${peerId.toString()}`);
42
+ this.logger.verbose(`Connected to transaction peer ${peerId.toString()}`);
40
43
  }
41
44
  });
42
45
 
@@ -44,9 +47,9 @@ export class PeerManager {
44
47
  this.libP2PNode.addEventListener('peer:disconnect', evt => {
45
48
  const peerId = evt.detail;
46
49
  if (this.peerDiscoveryService.isBootstrapPeer(peerId)) {
47
- this.logger.debug(`Disconnected from bootstrap peer ${peerId.toString()}`);
50
+ this.logger.verbose(`Disconnected from bootstrap peer ${peerId.toString()}`);
48
51
  } else {
49
- this.logger.debug(`Disconnected from transaction peer ${peerId.toString()}`);
52
+ this.logger.verbose(`Disconnected from transaction peer ${peerId.toString()}`);
50
53
  }
51
54
  });
52
55
 
@@ -57,6 +60,7 @@ export class PeerManager {
57
60
  }
58
61
 
59
62
  public heartbeat() {
63
+ this.heartbeatCounter++;
60
64
  this.discover();
61
65
  this.peerScoring.decayAllScores();
62
66
  }
@@ -64,13 +68,47 @@ export class PeerManager {
64
68
  public penalizePeer(peerId: PeerId, penalty: PeerErrorSeverity) {
65
69
  const id = peerId.toString();
66
70
  const penaltyValue = this.peerScoring.peerPenalties[penalty];
67
- this.peerScoring.updateScore(id, -penaltyValue);
71
+ const newScore = this.peerScoring.updateScore(id, -penaltyValue);
72
+ this.logger.verbose(`Penalizing peer ${id} with ${penalty} (new score is ${newScore})`);
68
73
  }
69
74
 
70
75
  public getPeerScore(peerId: string): number {
71
76
  return this.peerScoring.getScore(peerId);
72
77
  }
73
78
 
79
+ public getPeers(includePending = false): PeerInfo[] {
80
+ const connected = this.libP2PNode
81
+ .getPeers()
82
+ .map(peer => ({ id: peer.toString(), score: this.getPeerScore(peer.toString()), status: 'connected' as const }));
83
+
84
+ if (!includePending) {
85
+ return connected;
86
+ }
87
+
88
+ const dialQueue = this.libP2PNode
89
+ .getDialQueue()
90
+ .filter(peer => !!peer.peerId)
91
+ .map(peer => ({
92
+ id: peer.peerId!.toString(),
93
+ status: 'dialing' as const,
94
+ dialStatus: peer.status,
95
+ addresses: peer.multiaddrs.map(m => m.toString()),
96
+ }));
97
+
98
+ const cachedPeers = Array.from(this.cachedPeers.values())
99
+ .filter(peer => !dialQueue.some(dialPeer => dialPeer.id && peer.peerId.toString() === dialPeer.id.toString()))
100
+ .filter(peer => !connected.some(connPeer => connPeer.id.toString() === peer.peerId.toString()))
101
+ .map(peer => ({
102
+ status: 'cached' as const,
103
+ id: peer.peerId.toString(),
104
+ addresses: [peer.multiaddrTcp.toString()],
105
+ dialAttempts: peer.dialAttempts,
106
+ enr: peer.enr.encodeTxt(),
107
+ }));
108
+
109
+ return [...connected, ...dialQueue, ...cachedPeers];
110
+ }
111
+
74
112
  /**
75
113
  * Discovers peers.
76
114
  */
@@ -81,9 +119,13 @@ export class PeerManager {
81
119
  // Calculate how many connections we're looking to make
82
120
  const peersToConnect = this.config.maxPeerCount - connections.length;
83
121
 
84
- this.logger.debug(
85
- `Connections: ${connections.length}, Peers to connect: ${peersToConnect}, maxPeerCount: ${this.config.maxPeerCount}, cachedPeers: ${this.cachedPeers.size}`,
86
- );
122
+ const logLevel = this.heartbeatCounter % 60 === 0 ? 'info' : 'debug';
123
+ this.logger[logLevel](`Connected to ${connections.length} peers`, {
124
+ connections: connections.length,
125
+ maxPeerCount: this.config.maxPeerCount,
126
+ cachedPeers: this.cachedPeers.size,
127
+ ...this.peerScoring.getStats(),
128
+ });
87
129
 
88
130
  // Exit if no peers to connect
89
131
  if (peersToConnect <= 0) {
@@ -119,7 +161,7 @@ export class PeerManager {
119
161
 
120
162
  // if we need more peers, start randomNodesQuery
121
163
  if (peersToConnect > 0) {
122
- this.logger.debug('Running random nodes query');
164
+ this.logger.trace(`Running random nodes query to connect to ${peersToConnect} peers`);
123
165
  void this.peerDiscoveryService.runRandomNodesQuery();
124
166
  }
125
167
  }
@@ -134,23 +176,25 @@ export class PeerManager {
134
176
  // check if peer is already connected
135
177
  const [peerId, multiaddrTcp] = await Promise.all([enr.peerId(), enr.getFullMultiaddr('tcp')]);
136
178
 
137
- this.logger.debug(`Handling discovered peer ${peerId.toString()}, ${multiaddrTcp?.toString()}`);
179
+ this.logger.trace(
180
+ `Handling discovered peer ${peerId.toString()} at ${multiaddrTcp?.toString() ?? 'undefined address'}`,
181
+ );
138
182
 
139
183
  // throw if no tcp addr in multiaddr
140
184
  if (!multiaddrTcp) {
141
- this.logger.debug(`No TCP address in discovered node's multiaddr: ${enr.toString()}`);
185
+ this.logger.debug(`No TCP address in discovered node's multiaddr ${enr.encodeTxt()}`);
142
186
  return;
143
187
  }
144
188
  const connections = this.libP2PNode.getConnections();
145
189
  if (connections.some(conn => conn.remotePeer.equals(peerId))) {
146
- this.logger.debug(`Already connected to peer ${peerId.toString()}`);
190
+ this.logger.trace(`Already connected to peer ${peerId.toString()}`);
147
191
  return;
148
192
  }
149
193
 
150
194
  // check if peer is already in cache
151
195
  const id = peerId.toString();
152
196
  if (this.cachedPeers.has(id)) {
153
- this.logger.debug(`Already in cache ${id}`);
197
+ this.logger.trace(`Peer already in cache ${id}`);
154
198
  return;
155
199
  }
156
200
 
@@ -164,10 +208,9 @@ export class PeerManager {
164
208
 
165
209
  // Determine if we should dial immediately or not
166
210
  if (this.shouldDialPeer()) {
167
- this.logger.debug(`Dialing peer ${id}`);
168
211
  void this.dialPeer(cachedPeer);
169
212
  } else {
170
- this.logger.debug(`Caching peer ${id}`);
213
+ this.logger.trace(`Caching peer ${id}`);
171
214
  this.cachedPeers.set(id, cachedPeer);
172
215
  // Prune set of cached peers
173
216
  this.pruneCachedPeers();
@@ -178,15 +221,16 @@ export class PeerManager {
178
221
  const id = peer.peerId.toString();
179
222
  await this.libP2PNode.peerStore.merge(peer.peerId, { multiaddrs: [peer.multiaddrTcp] });
180
223
 
181
- this.logger.debug(`Dialing peer ${id}`);
224
+ this.logger.trace(`Dialing peer ${id}`);
182
225
  try {
183
226
  await this.libP2PNode.dial(peer.multiaddrTcp);
184
- } catch {
185
- this.logger.debug(`Failed to dial peer ${id}`);
227
+ } catch (error) {
186
228
  peer.dialAttempts++;
187
229
  if (peer.dialAttempts < MAX_DIAL_ATTEMPTS) {
230
+ this.logger.trace(`Failed to dial peer ${id} (attempt ${peer.dialAttempts})`, { error: inspect(error) });
188
231
  this.cachedPeers.set(id, peer);
189
232
  } else {
233
+ this.logger.debug(`Failed to dial peer ${id} (dropping)`, { error: inspect(error) });
190
234
  this.cachedPeers.delete(id);
191
235
  }
192
236
  }
@@ -194,9 +238,10 @@ export class PeerManager {
194
238
 
195
239
  private shouldDialPeer(): boolean {
196
240
  const connections = this.libP2PNode.getConnections().length;
197
- this.logger.debug(`Connections: ${connections}, maxPeerCount: ${this.config.maxPeerCount}`);
198
241
  if (connections >= this.config.maxPeerCount) {
199
- this.logger.debug('Not dialing peer, maxPeerCount reached');
242
+ this.logger.trace(
243
+ `Not dialing peer due to max peer count of ${this.config.maxPeerCount} reached (${connections} current connections)`,
244
+ );
200
245
  return false;
201
246
  }
202
247
  return true;
@@ -211,6 +256,7 @@ export class PeerManager {
211
256
  // Remove the oldest peers
212
257
  for (const key of this.cachedPeers.keys()) {
213
258
  this.cachedPeers.delete(key);
259
+ this.logger.trace(`Pruning peer ${key} from cache`);
214
260
  peersToDelete--;
215
261
  if (peersToDelete <= 0) {
216
262
  break;
@@ -1,3 +1,5 @@
1
+ import { median } from '@aztec/foundation/collection';
2
+
1
3
  import { type P2PConfig } from '../config.js';
2
4
 
3
5
  export enum PeerErrorSeverity {
@@ -43,7 +45,7 @@ export class PeerScoring {
43
45
  };
44
46
  }
45
47
 
46
- updateScore(peerId: string, scoreDelta: number): void {
48
+ updateScore(peerId: string, scoreDelta: number): number {
47
49
  const currentTime = Date.now();
48
50
  const lastUpdate = this.lastUpdateTime.get(peerId) || currentTime;
49
51
  const timePassed = currentTime - lastUpdate;
@@ -59,6 +61,7 @@ export class PeerScoring {
59
61
 
60
62
  this.scores.set(peerId, currentScore);
61
63
  this.lastUpdateTime.set(peerId, currentTime);
64
+ return currentScore;
62
65
  }
63
66
 
64
67
  decayAllScores(): void {
@@ -78,4 +81,8 @@ export class PeerScoring {
78
81
  getScore(peerId: string): number {
79
82
  return this.scores.get(peerId) || 0;
80
83
  }
84
+
85
+ getStats(): { medianScore: number } {
86
+ return { medianScore: median(Array.from(this.scores.values())) ?? 0 };
87
+ }
81
88
  }
@@ -1,5 +1,5 @@
1
1
  // @attribution: lodestar impl for inspiration
2
- import { type Logger, createDebugLogger } from '@aztec/foundation/log';
2
+ import { type Logger, createLogger } from '@aztec/foundation/log';
3
3
  import { executeTimeoutWithCustomError } from '@aztec/foundation/timer';
4
4
 
5
5
  import { type IncomingStreamData, type PeerId, type Stream } from '@libp2p/interface';
@@ -50,7 +50,7 @@ export class ReqResp {
50
50
  private rateLimiter: RequestResponseRateLimiter;
51
51
 
52
52
  constructor(config: P2PReqRespConfig, protected readonly libp2p: Libp2p, private peerManager: PeerManager) {
53
- this.logger = createDebugLogger('aztec:p2p:reqresp');
53
+ this.logger = createLogger('p2p:reqresp');
54
54
 
55
55
  this.overallRequestTimeoutMs = config.overallRequestTimeoutMs;
56
56
  this.individualRequestTimeoutMs = config.individualRequestTimeoutMs;
@@ -196,7 +196,7 @@ export class ReqResp {
196
196
  let stream: Stream | undefined;
197
197
  try {
198
198
  stream = await this.libp2p.dialProtocol(peerId, subProtocol);
199
- this.logger.debug(`Stream opened with ${peerId.toString()} for ${subProtocol}`);
199
+ this.logger.trace(`Stream opened with ${peerId.toString()} for ${subProtocol}`);
200
200
 
201
201
  // Open the stream with a timeout
202
202
  const result = await executeTimeoutWithCustomError<Buffer>(
@@ -206,17 +206,17 @@ export class ReqResp {
206
206
  );
207
207
 
208
208
  await stream.close();
209
- this.logger.debug(`Stream closed with ${peerId.toString()} for ${subProtocol}`);
209
+ this.logger.trace(`Stream closed with ${peerId.toString()} for ${subProtocol}`);
210
210
 
211
211
  return result;
212
212
  } catch (e: any) {
213
- this.logger.error(`${e.message} | peerId: ${peerId.toString()} | subProtocol: ${subProtocol}`);
213
+ this.logger.error(`Error sending request to peer`, e, { peerId: peerId.toString(), subProtocol });
214
214
  this.peerManager.penalizePeer(peerId, PeerErrorSeverity.HighToleranceError);
215
215
  } finally {
216
216
  if (stream) {
217
217
  try {
218
218
  await stream.close();
219
- this.logger.debug(`Stream closed with ${peerId.toString()} for ${subProtocol}`);
219
+ this.logger.trace(`Stream closed with ${peerId.toString()} for ${subProtocol}`);
220
220
  } catch (closeError) {
221
221
  this.logger.error(
222
222
  `Error closing stream: ${closeError instanceof Error ? closeError.message : 'Unknown error'}`,
@@ -1,4 +1,4 @@
1
- import type { BlockAttestation, BlockProposal, Gossipable } from '@aztec/circuit-types';
1
+ import type { BlockAttestation, BlockProposal, Gossipable, PeerInfo } from '@aztec/circuit-types';
2
2
 
3
3
  import type { ENR } from '@chainsafe/enr';
4
4
  import type { PeerId } from '@libp2p/interface';
@@ -49,6 +49,8 @@ export interface P2PService {
49
49
  registerBlockReceivedCallback(callback: (block: BlockProposal) => Promise<BlockAttestation | undefined>): void;
50
50
 
51
51
  getEnr(): ENR | undefined;
52
+
53
+ getPeers(includePending?: boolean): PeerInfo[];
52
54
  }
53
55
 
54
56
  /**
@@ -1,8 +1,8 @@
1
1
  import { Tx, type TxValidator } from '@aztec/circuit-types';
2
- import { createDebugLogger } from '@aztec/foundation/log';
2
+ import { createLogger } from '@aztec/foundation/log';
3
3
 
4
4
  export class DataTxValidator implements TxValidator<Tx> {
5
- #log = createDebugLogger('aztec:sequencer:tx_validator:tx_data');
5
+ #log = createLogger('p2p:tx_validator:tx_data');
6
6
 
7
7
  validateTxs(txs: Tx[]): Promise<[validTxs: Tx[], invalidTxs: Tx[]]> {
8
8
  const validTxs: Tx[] = [];
@@ -1,13 +1,13 @@
1
1
  import { type AnyTx, Tx, type TxValidator } from '@aztec/circuit-types';
2
2
  import { Fr } from '@aztec/circuits.js';
3
- import { createDebugLogger } from '@aztec/foundation/log';
3
+ import { createLogger } from '@aztec/foundation/log';
4
4
 
5
5
  export interface NullifierSource {
6
6
  getNullifierIndex: (nullifier: Fr) => Promise<bigint | undefined>;
7
7
  }
8
8
 
9
9
  export class DoubleSpendTxValidator<T extends AnyTx> implements TxValidator<T> {
10
- #log = createDebugLogger('aztec:sequencer:tx_validator:tx_double_spend');
10
+ #log = createLogger('p2p:tx_validator:tx_double_spend');
11
11
  #nullifierSource: NullifierSource;
12
12
 
13
13
  constructor(nullifierSource: NullifierSource, private readonly isValidatingBlock: boolean = true) {
@@ -1,9 +1,9 @@
1
1
  import { type AnyTx, Tx, type TxValidator } from '@aztec/circuit-types';
2
2
  import { type Fr } from '@aztec/circuits.js';
3
- import { createDebugLogger } from '@aztec/foundation/log';
3
+ import { createLogger } from '@aztec/foundation/log';
4
4
 
5
5
  export class MetadataTxValidator<T extends AnyTx> implements TxValidator<T> {
6
- #log = createDebugLogger('aztec:sequencer:tx_validator:tx_metadata');
6
+ #log = createLogger('p2p:tx_validator:tx_metadata');
7
7
 
8
8
  constructor(private chainId: Fr, private blockNumber: Fr) {}
9
9
 
@@ -1,8 +1,8 @@
1
1
  import { type ClientProtocolCircuitVerifier, Tx, type TxValidator } from '@aztec/circuit-types';
2
- import { createDebugLogger } from '@aztec/foundation/log';
2
+ import { createLogger } from '@aztec/foundation/log';
3
3
 
4
4
  export class TxProofValidator implements TxValidator<Tx> {
5
- #log = createDebugLogger('aztec:sequencer:tx_validator:private_proof');
5
+ #log = createLogger('p2p:tx_validator:private_proof');
6
6
 
7
7
  constructor(private verifier: ClientProtocolCircuitVerifier) {}
8
8