@aztec/p2p 0.66.0 → 0.67.1-devnet

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 (163) 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 +21 -9
  4. package/dest/client/index.d.ts +5 -4
  5. package/dest/client/index.d.ts.map +1 -1
  6. package/dest/client/index.js +18 -12
  7. package/dest/client/p2p_client.d.ts +13 -20
  8. package/dest/client/p2p_client.d.ts.map +1 -1
  9. package/dest/client/p2p_client.js +32 -15
  10. package/dest/config.d.ts +1 -1
  11. package/dest/config.d.ts.map +1 -1
  12. package/dest/config.js +2 -2
  13. package/dest/errors/reqresp.error.d.ts +12 -1
  14. package/dest/errors/reqresp.error.d.ts.map +1 -1
  15. package/dest/errors/reqresp.error.js +15 -2
  16. package/dest/index.d.ts +1 -1
  17. package/dest/index.d.ts.map +1 -1
  18. package/dest/index.js +2 -2
  19. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +9 -0
  20. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  21. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +3 -0
  22. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -0
  23. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +171 -0
  24. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +29 -0
  25. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -0
  26. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +114 -0
  27. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
  28. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +3 -3
  29. package/dest/mem_pools/instrumentation.d.ts.map +1 -1
  30. package/dest/mem_pools/instrumentation.js +2 -20
  31. package/dest/mem_pools/interface.d.ts +4 -3
  32. package/dest/mem_pools/interface.d.ts.map +1 -1
  33. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
  34. package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +4 -4
  35. package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
  36. package/dest/mem_pools/tx_pool/memory_tx_pool.js +4 -4
  37. package/dest/mocks/index.d.ts +6 -6
  38. package/dest/mocks/index.d.ts.map +1 -1
  39. package/dest/mocks/index.js +9 -9
  40. package/dest/services/data_store.d.ts.map +1 -0
  41. package/dest/services/data_store.js +188 -0
  42. package/dest/{service → services/discv5}/discV5_service.d.ts +4 -2
  43. package/dest/services/discv5/discV5_service.d.ts.map +1 -0
  44. package/dest/services/discv5/discV5_service.js +144 -0
  45. package/dest/{service → services}/dummy_service.d.ts +3 -1
  46. package/dest/services/dummy_service.d.ts.map +1 -0
  47. package/dest/{service → services}/dummy_service.js +5 -1
  48. package/dest/{service → services}/encoding.d.ts +5 -0
  49. package/dest/services/encoding.d.ts.map +1 -0
  50. package/dest/services/encoding.js +65 -0
  51. package/dest/services/index.d.ts +3 -0
  52. package/dest/services/index.d.ts.map +1 -0
  53. package/dest/services/index.js +3 -0
  54. package/dest/{service → services/libp2p}/libp2p_service.d.ts +50 -11
  55. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -0
  56. package/dest/services/libp2p/libp2p_service.js +573 -0
  57. package/dest/{service → services/peer-scoring}/peer_scoring.d.ts +5 -2
  58. package/dest/services/peer-scoring/peer_scoring.d.ts.map +1 -0
  59. package/dest/services/peer-scoring/peer_scoring.js +72 -0
  60. package/dest/{service → services}/peer_manager.d.ts +8 -3
  61. package/dest/services/peer_manager.d.ts.map +1 -0
  62. package/dest/services/peer_manager.js +230 -0
  63. package/dest/services/reqresp/config.d.ts.map +1 -0
  64. package/dest/{service → services}/reqresp/config.js +1 -1
  65. package/dest/services/reqresp/handlers.d.ts.map +1 -0
  66. package/dest/{service → services}/reqresp/handlers.js +1 -1
  67. package/dest/services/reqresp/index.d.ts.map +1 -0
  68. package/dest/{service → services}/reqresp/index.js +1 -1
  69. package/dest/services/reqresp/interface.d.ts.map +1 -0
  70. package/dest/{service → services}/reqresp/interface.js +1 -1
  71. package/dest/services/reqresp/rate_limiter/index.d.ts.map +1 -0
  72. package/dest/{service → services}/reqresp/rate_limiter/index.js +1 -1
  73. package/dest/services/reqresp/rate_limiter/rate_limiter.d.ts.map +1 -0
  74. package/dest/{service → services}/reqresp/rate_limiter/rate_limiter.js +2 -2
  75. package/dest/services/reqresp/rate_limiter/rate_limits.d.ts.map +1 -0
  76. package/dest/{service → services}/reqresp/rate_limiter/rate_limits.js +1 -1
  77. package/dest/{service → services}/reqresp/reqresp.d.ts +16 -0
  78. package/dest/services/reqresp/reqresp.d.ts.map +1 -0
  79. package/dest/{service → services}/reqresp/reqresp.js +69 -20
  80. package/dest/{service → services}/service.d.ts +2 -1
  81. package/dest/services/service.d.ts.map +1 -0
  82. package/dest/{service → services}/service.js +1 -1
  83. package/dest/tx_validator/aggregate_tx_validator.d.ts +1 -1
  84. package/dest/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
  85. package/dest/tx_validator/aggregate_tx_validator.js +5 -3
  86. package/dest/tx_validator/data_validator.js +3 -3
  87. package/dest/tx_validator/double_spend_validator.d.ts +3 -2
  88. package/dest/tx_validator/double_spend_validator.d.ts.map +1 -1
  89. package/dest/tx_validator/double_spend_validator.js +8 -8
  90. package/dest/tx_validator/metadata_validator.js +3 -3
  91. package/dest/tx_validator/tx_proof_validator.js +3 -3
  92. package/package.json +12 -8
  93. package/src/bootstrap/bootstrap.ts +24 -10
  94. package/src/client/index.ts +44 -19
  95. package/src/client/p2p_client.ts +58 -36
  96. package/src/config.ts +1 -1
  97. package/src/errors/reqresp.error.ts +15 -1
  98. package/src/index.ts +1 -1
  99. package/src/mem_pools/attestation_pool/attestation_pool.ts +10 -0
  100. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +237 -0
  101. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +153 -0
  102. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +2 -2
  103. package/src/mem_pools/instrumentation.ts +1 -21
  104. package/src/mem_pools/interface.ts +5 -3
  105. package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +3 -3
  106. package/src/mem_pools/tx_pool/memory_tx_pool.ts +3 -3
  107. package/src/mocks/index.ts +14 -11
  108. package/src/{service → services/discv5}/discV5_service.ts +20 -15
  109. package/src/{service → services}/dummy_service.ts +6 -1
  110. package/src/{service → services}/encoding.ts +21 -3
  111. package/src/services/index.ts +2 -0
  112. package/src/{service → services/libp2p}/libp2p_service.ts +208 -96
  113. package/src/{service → services/peer-scoring}/peer_scoring.ts +9 -2
  114. package/src/{service → services}/peer_manager.ts +73 -24
  115. package/src/{service → services}/reqresp/rate_limiter/rate_limiter.ts +1 -1
  116. package/src/{service → services}/reqresp/reqresp.ts +87 -21
  117. package/src/{service → services}/service.ts +3 -1
  118. package/src/tx_validator/aggregate_tx_validator.ts +5 -3
  119. package/src/tx_validator/data_validator.ts +2 -2
  120. package/src/tx_validator/double_spend_validator.ts +8 -10
  121. package/src/tx_validator/metadata_validator.ts +2 -2
  122. package/src/tx_validator/tx_proof_validator.ts +2 -2
  123. package/dest/service/data_store.d.ts.map +0 -1
  124. package/dest/service/data_store.js +0 -188
  125. package/dest/service/discV5_service.d.ts.map +0 -1
  126. package/dest/service/discV5_service.js +0 -141
  127. package/dest/service/dummy_service.d.ts.map +0 -1
  128. package/dest/service/encoding.d.ts.map +0 -1
  129. package/dest/service/encoding.js +0 -49
  130. package/dest/service/index.d.ts +0 -3
  131. package/dest/service/index.d.ts.map +0 -1
  132. package/dest/service/index.js +0 -3
  133. package/dest/service/libp2p_service.d.ts.map +0 -1
  134. package/dest/service/libp2p_service.js +0 -496
  135. package/dest/service/peer_manager.d.ts.map +0 -1
  136. package/dest/service/peer_manager.js +0 -176
  137. package/dest/service/peer_scoring.d.ts.map +0 -1
  138. package/dest/service/peer_scoring.js +0 -67
  139. package/dest/service/reqresp/config.d.ts.map +0 -1
  140. package/dest/service/reqresp/handlers.d.ts.map +0 -1
  141. package/dest/service/reqresp/index.d.ts.map +0 -1
  142. package/dest/service/reqresp/interface.d.ts.map +0 -1
  143. package/dest/service/reqresp/rate_limiter/index.d.ts.map +0 -1
  144. package/dest/service/reqresp/rate_limiter/rate_limiter.d.ts.map +0 -1
  145. package/dest/service/reqresp/rate_limiter/rate_limits.d.ts.map +0 -1
  146. package/dest/service/reqresp/reqresp.d.ts.map +0 -1
  147. package/dest/service/service.d.ts.map +0 -1
  148. package/src/service/index.ts +0 -2
  149. /package/dest/{service → services}/data_store.d.ts +0 -0
  150. /package/dest/{service → services}/reqresp/config.d.ts +0 -0
  151. /package/dest/{service → services}/reqresp/handlers.d.ts +0 -0
  152. /package/dest/{service → services}/reqresp/index.d.ts +0 -0
  153. /package/dest/{service → services}/reqresp/interface.d.ts +0 -0
  154. /package/dest/{service → services}/reqresp/rate_limiter/index.d.ts +0 -0
  155. /package/dest/{service → services}/reqresp/rate_limiter/rate_limiter.d.ts +0 -0
  156. /package/dest/{service → services}/reqresp/rate_limiter/rate_limits.d.ts +0 -0
  157. /package/src/{service → services}/data_store.ts +0 -0
  158. /package/src/{service → services}/reqresp/config.ts +0 -0
  159. /package/src/{service → services}/reqresp/handlers.ts +0 -0
  160. /package/src/{service → services}/reqresp/index.ts +0 -0
  161. /package/src/{service → services}/reqresp/interface.ts +0 -0
  162. /package/src/{service → services}/reqresp/rate_limiter/index.ts +0 -0
  163. /package/src/{service → services}/reqresp/rate_limiter/rate_limits.ts +0 -0
@@ -1,12 +1,15 @@
1
- import { createDebugLogger } from '@aztec/foundation/log';
1
+ import { type PeerInfo } from '@aztec/circuit-types';
2
+ import { createLogger } from '@aztec/foundation/log';
3
+ import { type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
2
4
 
3
5
  import { type ENR } from '@chainsafe/enr';
4
6
  import { type PeerId } from '@libp2p/interface';
5
7
  import { type Multiaddr } from '@multiformats/multiaddr';
8
+ import { inspect } from 'util';
6
9
 
7
10
  import { type P2PConfig } from '../config.js';
8
11
  import { type PubSubLibp2p } from '../util.js';
9
- import { type PeerErrorSeverity, PeerScoring } from './peer_scoring.js';
12
+ import { type PeerErrorSeverity, PeerScoring } from './peer-scoring/peer_scoring.js';
10
13
  import { type PeerDiscoveryService } from './service.js';
11
14
 
12
15
  const MAX_DIAL_ATTEMPTS = 3;
@@ -19,24 +22,26 @@ type CachedPeer = {
19
22
  dialAttempts: number;
20
23
  };
21
24
 
22
- export class PeerManager {
25
+ export class PeerManager implements Traceable {
23
26
  private cachedPeers: Map<string, CachedPeer> = new Map();
24
27
  private peerScoring: PeerScoring;
28
+ private heartbeatCounter: number = 0;
25
29
 
26
30
  constructor(
27
31
  private libP2PNode: PubSubLibp2p,
28
32
  private peerDiscoveryService: PeerDiscoveryService,
29
33
  private config: P2PConfig,
30
- private logger = createDebugLogger('aztec:p2p:peer_manager'),
34
+ public readonly tracer: Tracer,
35
+ private logger = createLogger('p2p:peer-manager'),
31
36
  ) {
32
37
  this.peerScoring = new PeerScoring(config);
33
38
  // Handle new established connections
34
39
  this.libP2PNode.addEventListener('peer:connect', evt => {
35
40
  const peerId = evt.detail;
36
41
  if (this.peerDiscoveryService.isBootstrapPeer(peerId)) {
37
- this.logger.debug(`Connected to bootstrap peer ${peerId.toString()}`);
42
+ this.logger.verbose(`Connected to bootstrap peer ${peerId.toString()}`);
38
43
  } else {
39
- this.logger.debug(`Connected to transaction peer ${peerId.toString()}`);
44
+ this.logger.verbose(`Connected to transaction peer ${peerId.toString()}`);
40
45
  }
41
46
  });
42
47
 
@@ -44,9 +49,9 @@ export class PeerManager {
44
49
  this.libP2PNode.addEventListener('peer:disconnect', evt => {
45
50
  const peerId = evt.detail;
46
51
  if (this.peerDiscoveryService.isBootstrapPeer(peerId)) {
47
- this.logger.debug(`Disconnected from bootstrap peer ${peerId.toString()}`);
52
+ this.logger.verbose(`Disconnected from bootstrap peer ${peerId.toString()}`);
48
53
  } else {
49
- this.logger.debug(`Disconnected from transaction peer ${peerId.toString()}`);
54
+ this.logger.verbose(`Disconnected from transaction peer ${peerId.toString()}`);
50
55
  }
51
56
  });
52
57
 
@@ -56,7 +61,9 @@ export class PeerManager {
56
61
  });
57
62
  }
58
63
 
64
+ @trackSpan('PeerManager.heartbeat')
59
65
  public heartbeat() {
66
+ this.heartbeatCounter++;
60
67
  this.discover();
61
68
  this.peerScoring.decayAllScores();
62
69
  }
@@ -64,13 +71,47 @@ export class PeerManager {
64
71
  public penalizePeer(peerId: PeerId, penalty: PeerErrorSeverity) {
65
72
  const id = peerId.toString();
66
73
  const penaltyValue = this.peerScoring.peerPenalties[penalty];
67
- this.peerScoring.updateScore(id, -penaltyValue);
74
+ const newScore = this.peerScoring.updateScore(id, -penaltyValue);
75
+ this.logger.verbose(`Penalizing peer ${id} with ${penalty} (new score is ${newScore})`);
68
76
  }
69
77
 
70
78
  public getPeerScore(peerId: string): number {
71
79
  return this.peerScoring.getScore(peerId);
72
80
  }
73
81
 
82
+ public getPeers(includePending = false): PeerInfo[] {
83
+ const connected = this.libP2PNode
84
+ .getPeers()
85
+ .map(peer => ({ id: peer.toString(), score: this.getPeerScore(peer.toString()), status: 'connected' as const }));
86
+
87
+ if (!includePending) {
88
+ return connected;
89
+ }
90
+
91
+ const dialQueue = this.libP2PNode
92
+ .getDialQueue()
93
+ .filter(peer => !!peer.peerId)
94
+ .map(peer => ({
95
+ id: peer.peerId!.toString(),
96
+ status: 'dialing' as const,
97
+ dialStatus: peer.status,
98
+ addresses: peer.multiaddrs.map(m => m.toString()),
99
+ }));
100
+
101
+ const cachedPeers = Array.from(this.cachedPeers.values())
102
+ .filter(peer => !dialQueue.some(dialPeer => dialPeer.id && peer.peerId.toString() === dialPeer.id.toString()))
103
+ .filter(peer => !connected.some(connPeer => connPeer.id.toString() === peer.peerId.toString()))
104
+ .map(peer => ({
105
+ status: 'cached' as const,
106
+ id: peer.peerId.toString(),
107
+ addresses: [peer.multiaddrTcp.toString()],
108
+ dialAttempts: peer.dialAttempts,
109
+ enr: peer.enr.encodeTxt(),
110
+ }));
111
+
112
+ return [...connected, ...dialQueue, ...cachedPeers];
113
+ }
114
+
74
115
  /**
75
116
  * Discovers peers.
76
117
  */
@@ -81,9 +122,13 @@ export class PeerManager {
81
122
  // Calculate how many connections we're looking to make
82
123
  const peersToConnect = this.config.maxPeerCount - connections.length;
83
124
 
84
- this.logger.debug(
85
- `Connections: ${connections.length}, Peers to connect: ${peersToConnect}, maxPeerCount: ${this.config.maxPeerCount}, cachedPeers: ${this.cachedPeers.size}`,
86
- );
125
+ const logLevel = this.heartbeatCounter % 60 === 0 ? 'info' : 'debug';
126
+ this.logger[logLevel](`Connected to ${connections.length} peers`, {
127
+ connections: connections.length,
128
+ maxPeerCount: this.config.maxPeerCount,
129
+ cachedPeers: this.cachedPeers.size,
130
+ ...this.peerScoring.getStats(),
131
+ });
87
132
 
88
133
  // Exit if no peers to connect
89
134
  if (peersToConnect <= 0) {
@@ -119,7 +164,7 @@ export class PeerManager {
119
164
 
120
165
  // if we need more peers, start randomNodesQuery
121
166
  if (peersToConnect > 0) {
122
- this.logger.debug('Running random nodes query');
167
+ this.logger.trace(`Running random nodes query to connect to ${peersToConnect} peers`);
123
168
  void this.peerDiscoveryService.runRandomNodesQuery();
124
169
  }
125
170
  }
@@ -134,23 +179,25 @@ export class PeerManager {
134
179
  // check if peer is already connected
135
180
  const [peerId, multiaddrTcp] = await Promise.all([enr.peerId(), enr.getFullMultiaddr('tcp')]);
136
181
 
137
- this.logger.debug(`Handling discovered peer ${peerId.toString()}, ${multiaddrTcp?.toString()}`);
182
+ this.logger.trace(
183
+ `Handling discovered peer ${peerId.toString()} at ${multiaddrTcp?.toString() ?? 'undefined address'}`,
184
+ );
138
185
 
139
186
  // throw if no tcp addr in multiaddr
140
187
  if (!multiaddrTcp) {
141
- this.logger.debug(`No TCP address in discovered node's multiaddr: ${enr.toString()}`);
188
+ this.logger.debug(`No TCP address in discovered node's multiaddr ${enr.encodeTxt()}`);
142
189
  return;
143
190
  }
144
191
  const connections = this.libP2PNode.getConnections();
145
192
  if (connections.some(conn => conn.remotePeer.equals(peerId))) {
146
- this.logger.debug(`Already connected to peer ${peerId.toString()}`);
193
+ this.logger.trace(`Already connected to peer ${peerId.toString()}`);
147
194
  return;
148
195
  }
149
196
 
150
197
  // check if peer is already in cache
151
198
  const id = peerId.toString();
152
199
  if (this.cachedPeers.has(id)) {
153
- this.logger.debug(`Already in cache ${id}`);
200
+ this.logger.trace(`Peer already in cache ${id}`);
154
201
  return;
155
202
  }
156
203
 
@@ -164,10 +211,9 @@ export class PeerManager {
164
211
 
165
212
  // Determine if we should dial immediately or not
166
213
  if (this.shouldDialPeer()) {
167
- this.logger.debug(`Dialing peer ${id}`);
168
214
  void this.dialPeer(cachedPeer);
169
215
  } else {
170
- this.logger.debug(`Caching peer ${id}`);
216
+ this.logger.trace(`Caching peer ${id}`);
171
217
  this.cachedPeers.set(id, cachedPeer);
172
218
  // Prune set of cached peers
173
219
  this.pruneCachedPeers();
@@ -178,15 +224,16 @@ export class PeerManager {
178
224
  const id = peer.peerId.toString();
179
225
  await this.libP2PNode.peerStore.merge(peer.peerId, { multiaddrs: [peer.multiaddrTcp] });
180
226
 
181
- this.logger.debug(`Dialing peer ${id}`);
227
+ this.logger.trace(`Dialing peer ${id}`);
182
228
  try {
183
229
  await this.libP2PNode.dial(peer.multiaddrTcp);
184
- } catch {
185
- this.logger.debug(`Failed to dial peer ${id}`);
230
+ } catch (error) {
186
231
  peer.dialAttempts++;
187
232
  if (peer.dialAttempts < MAX_DIAL_ATTEMPTS) {
233
+ this.logger.trace(`Failed to dial peer ${id} (attempt ${peer.dialAttempts})`, { error: inspect(error) });
188
234
  this.cachedPeers.set(id, peer);
189
235
  } else {
236
+ this.logger.debug(`Failed to dial peer ${id} (dropping)`, { error: inspect(error) });
190
237
  this.cachedPeers.delete(id);
191
238
  }
192
239
  }
@@ -194,9 +241,10 @@ export class PeerManager {
194
241
 
195
242
  private shouldDialPeer(): boolean {
196
243
  const connections = this.libP2PNode.getConnections().length;
197
- this.logger.debug(`Connections: ${connections}, maxPeerCount: ${this.config.maxPeerCount}`);
198
244
  if (connections >= this.config.maxPeerCount) {
199
- this.logger.debug('Not dialing peer, maxPeerCount reached');
245
+ this.logger.trace(
246
+ `Not dialing peer due to max peer count of ${this.config.maxPeerCount} reached (${connections} current connections)`,
247
+ );
200
248
  return false;
201
249
  }
202
250
  return true;
@@ -211,6 +259,7 @@ export class PeerManager {
211
259
  // Remove the oldest peers
212
260
  for (const key of this.cachedPeers.keys()) {
213
261
  this.cachedPeers.delete(key);
262
+ this.logger.trace(`Pruning peer ${key} from cache`);
214
263
  peersToDelete--;
215
264
  if (peersToDelete <= 0) {
216
265
  break;
@@ -5,8 +5,8 @@
5
5
  */
6
6
  import { type PeerId } from '@libp2p/interface';
7
7
 
8
+ import { PeerErrorSeverity } from '../../peer-scoring/peer_scoring.js';
8
9
  import { type PeerManager } from '../../peer_manager.js';
9
- import { PeerErrorSeverity } from '../../peer_scoring.js';
10
10
  import { type ReqRespSubProtocol, type ReqRespSubProtocolRateLimits } from '../interface.js';
11
11
  import { DEFAULT_RATE_LIMITS } from './rate_limits.js';
12
12
 
@@ -1,16 +1,20 @@
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';
6
6
  import { pipe } from 'it-pipe';
7
7
  import { type Libp2p } from 'libp2p';
8
- import { compressSync, uncompressSync } from 'snappy';
9
8
  import { type Uint8ArrayList } from 'uint8arraylist';
10
9
 
11
- import { CollectiveReqRespTimeoutError, IndiviualReqRespTimeoutError } from '../../errors/reqresp.error.js';
10
+ import {
11
+ CollectiveReqRespTimeoutError,
12
+ IndividualReqRespTimeoutError,
13
+ InvalidResponseError,
14
+ } from '../../errors/reqresp.error.js';
15
+ import { SnappyTransform } from '../encoding.js';
16
+ import { PeerErrorSeverity } from '../peer-scoring/peer_scoring.js';
12
17
  import { type PeerManager } from '../peer_manager.js';
13
- import { PeerErrorSeverity } from '../peer_scoring.js';
14
18
  import { type P2PReqRespConfig } from './config.js';
15
19
  import {
16
20
  DEFAULT_SUB_PROTOCOL_HANDLERS,
@@ -49,13 +53,16 @@ export class ReqResp {
49
53
 
50
54
  private rateLimiter: RequestResponseRateLimiter;
51
55
 
56
+ private snappyTransform: SnappyTransform;
57
+
52
58
  constructor(config: P2PReqRespConfig, protected readonly libp2p: Libp2p, private peerManager: PeerManager) {
53
- this.logger = createDebugLogger('aztec:p2p:reqresp');
59
+ this.logger = createLogger('p2p:reqresp');
54
60
 
55
61
  this.overallRequestTimeoutMs = config.overallRequestTimeoutMs;
56
62
  this.individualRequestTimeoutMs = config.individualRequestTimeoutMs;
57
63
 
58
64
  this.rateLimiter = new RequestResponseRateLimiter(peerManager);
65
+ this.snappyTransform = new SnappyTransform();
59
66
  }
60
67
 
61
68
  /**
@@ -143,8 +150,7 @@ export class ReqResp {
143
150
  // The response validator handles peer punishment within
144
151
  const isValid = await responseValidator(request, object, peer);
145
152
  if (!isValid) {
146
- this.logger.error(`Invalid response for ${subProtocol} from ${peer.toString()}`);
147
- return undefined;
153
+ throw new InvalidResponseError();
148
154
  }
149
155
  return object;
150
156
  }
@@ -159,7 +165,7 @@ export class ReqResp {
159
165
  () => new CollectiveReqRespTimeoutError(),
160
166
  );
161
167
  } catch (e: any) {
162
- this.logger.error(`${e.message} | subProtocol: ${subProtocol}`);
168
+ this.logger.debug(`${e.message} | subProtocol: ${subProtocol}`);
163
169
  return undefined;
164
170
  }
165
171
  }
@@ -196,27 +202,23 @@ export class ReqResp {
196
202
  let stream: Stream | undefined;
197
203
  try {
198
204
  stream = await this.libp2p.dialProtocol(peerId, subProtocol);
199
- this.logger.debug(`Stream opened with ${peerId.toString()} for ${subProtocol}`);
205
+ this.logger.trace(`Stream opened with ${peerId.toString()} for ${subProtocol}`);
200
206
 
201
207
  // Open the stream with a timeout
202
208
  const result = await executeTimeoutWithCustomError<Buffer>(
203
- (): Promise<Buffer> => pipe([payload], stream!, this.readMessage),
209
+ (): Promise<Buffer> => pipe([payload], stream!, this.readMessage.bind(this)),
204
210
  this.individualRequestTimeoutMs,
205
- () => new IndiviualReqRespTimeoutError(),
211
+ () => new IndividualReqRespTimeoutError(),
206
212
  );
207
213
 
208
- await stream.close();
209
- this.logger.debug(`Stream closed with ${peerId.toString()} for ${subProtocol}`);
210
-
211
214
  return result;
212
215
  } catch (e: any) {
213
- this.logger.error(`${e.message} | peerId: ${peerId.toString()} | subProtocol: ${subProtocol}`);
214
- this.peerManager.penalizePeer(peerId, PeerErrorSeverity.HighToleranceError);
216
+ this.handleResponseError(e, peerId, subProtocol);
215
217
  } finally {
216
218
  if (stream) {
217
219
  try {
218
220
  await stream.close();
219
- this.logger.debug(`Stream closed with ${peerId.toString()} for ${subProtocol}`);
221
+ this.logger.trace(`Stream closed with ${peerId.toString()} for ${subProtocol}`);
220
222
  } catch (closeError) {
221
223
  this.logger.error(
222
224
  `Error closing stream: ${closeError instanceof Error ? closeError.message : 'Unknown error'}`,
@@ -224,7 +226,70 @@ export class ReqResp {
224
226
  }
225
227
  }
226
228
  }
227
- return undefined;
229
+ }
230
+
231
+ /**
232
+ * Handle a response error
233
+ *
234
+ * ReqResp errors are punished differently depending on the severity of the offense
235
+ *
236
+ * @param e - The error
237
+ * @param peerId - The peer id
238
+ * @param subProtocol - The sub protocol
239
+ * @returns If the error is non pubishable, then undefined is returned, otherwise the peer is penalized
240
+ */
241
+ private handleResponseError(e: any, peerId: PeerId, subProtocol: ReqRespSubProtocol): void {
242
+ const severity = this.categorizeError(e, peerId, subProtocol);
243
+ if (severity) {
244
+ this.peerManager.penalizePeer(peerId, severity);
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Categorize the error and log it.
250
+ */
251
+ private categorizeError(e: any, peerId: PeerId, subProtocol: ReqRespSubProtocol): PeerErrorSeverity | undefined {
252
+ // Non pubishable errors
253
+ // We do not punish a collective timeout, as the node triggers this interupt, independent of the peer's behaviour
254
+ const logTags = {
255
+ peerId: peerId.toString(),
256
+ subProtocol,
257
+ };
258
+ if (e instanceof CollectiveReqRespTimeoutError || e instanceof InvalidResponseError) {
259
+ this.logger.debug(
260
+ `Non-punishable error: ${e.message} | peerId: ${peerId.toString()} | subProtocol: ${subProtocol}`,
261
+ logTags,
262
+ );
263
+ return undefined;
264
+ }
265
+
266
+ // Pubishable errors
267
+ // Connection reset errors in the networking stack are punished with high severity
268
+ // it just signals an unreliable peer
269
+ // We assume that the requesting node has a functioning networking stack.
270
+ if (e?.code === 'ECONNRESET' || e?.code === 'EPIPE') {
271
+ this.logger.debug(`Connection reset: ${peerId.toString()}`, logTags);
272
+ return PeerErrorSeverity.HighToleranceError;
273
+ }
274
+
275
+ if (e?.code === 'ECONNREFUSED') {
276
+ this.logger.debug(`Connection refused: ${peerId.toString()}`, logTags);
277
+ return PeerErrorSeverity.HighToleranceError;
278
+ }
279
+
280
+ // Timeout errors are punished with high tolerance, they can be due to a geogrpahically far away peer or an
281
+ // overloaded peer
282
+ if (e instanceof IndividualReqRespTimeoutError) {
283
+ this.logger.debug(
284
+ `Timeout error: ${e.message} | peerId: ${peerId.toString()} | subProtocol: ${subProtocol}`,
285
+ logTags,
286
+ );
287
+ return PeerErrorSeverity.HighToleranceError;
288
+ }
289
+
290
+ // Catch all error
291
+ this.logger.error(`Unexpected error sending request to peer`, e, logTags);
292
+ return PeerErrorSeverity.HighToleranceError;
228
293
  }
229
294
 
230
295
  /**
@@ -235,8 +300,8 @@ export class ReqResp {
235
300
  for await (const chunk of source) {
236
301
  chunks.push(chunk.subarray());
237
302
  }
238
- const messageData = chunks.concat();
239
- return uncompressSync(Buffer.concat(messageData), { asBuffer: true }) as Buffer;
303
+ const messageData = Buffer.concat(chunks);
304
+ return this.snappyTransform.inboundTransformNoTopic(messageData);
240
305
  }
241
306
 
242
307
  /**
@@ -266,6 +331,7 @@ export class ReqResp {
266
331
  }
267
332
 
268
333
  const handler = this.subProtocolHandlers[protocol];
334
+ const transform = this.snappyTransform;
269
335
 
270
336
  try {
271
337
  await pipe(
@@ -274,7 +340,7 @@ export class ReqResp {
274
340
  for await (const chunkList of source) {
275
341
  const msg = Buffer.from(chunkList.subarray());
276
342
  const response = await handler(msg);
277
- yield new Uint8Array(compressSync(response));
343
+ yield new Uint8Array(transform.outboundTransformNoTopic(response));
278
344
  }
279
345
  },
280
346
  stream,
@@ -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
  /**
@@ -10,16 +10,18 @@ export class AggregateTxValidator<T extends Tx | ProcessedTx> implements TxValid
10
10
  this.#validators = validators;
11
11
  }
12
12
 
13
- async validateTxs(txs: T[]): Promise<[validTxs: T[], invalidTxs: T[]]> {
13
+ async validateTxs(txs: T[]): Promise<[validTxs: T[], invalidTxs: T[], skippedTxs: T[]]> {
14
14
  const invalidTxs: T[] = [];
15
+ const skippedTxs: T[] = [];
15
16
  let txPool = txs;
16
17
  for (const validator of this.#validators) {
17
- const [valid, invalid] = await validator.validateTxs(txPool);
18
+ const [valid, invalid, skipped] = await validator.validateTxs(txPool);
18
19
  invalidTxs.push(...invalid);
20
+ skippedTxs.push(...(skipped ?? []));
19
21
  txPool = valid;
20
22
  }
21
23
 
22
- return [txPool, invalidTxs];
24
+ return [txPool, invalidTxs, skippedTxs];
23
25
  }
24
26
 
25
27
  async validateTx(tx: T): Promise<boolean> {
@@ -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,12 @@
1
1
  import { type AnyTx, Tx, type TxValidator } from '@aztec/circuit-types';
2
- import { Fr } from '@aztec/circuits.js';
3
- import { createDebugLogger } from '@aztec/foundation/log';
2
+ import { createLogger } from '@aztec/foundation/log';
4
3
 
5
4
  export interface NullifierSource {
6
- getNullifierIndex: (nullifier: Fr) => Promise<bigint | undefined>;
5
+ getNullifierIndices: (nullifiers: Buffer[]) => Promise<(bigint | undefined)[]>;
7
6
  }
8
7
 
9
8
  export class DoubleSpendTxValidator<T extends AnyTx> implements TxValidator<T> {
10
- #log = createDebugLogger('aztec:sequencer:tx_validator:tx_double_spend');
9
+ #log = createLogger('p2p:tx_validator:tx_double_spend');
11
10
  #nullifierSource: NullifierSource;
12
11
 
13
12
  constructor(nullifierSource: NullifierSource, private readonly isValidatingBlock: boolean = true) {
@@ -36,9 +35,7 @@ export class DoubleSpendTxValidator<T extends AnyTx> implements TxValidator<T> {
36
35
  }
37
36
 
38
37
  async #uniqueNullifiers(tx: AnyTx, thisBlockNullifiers: Set<bigint>): Promise<boolean> {
39
- const nullifiers = (tx instanceof Tx ? tx.data.getNonEmptyNullifiers() : tx.txEffect.nullifiers).map(x =>
40
- x.toBigInt(),
41
- );
38
+ const nullifiers = tx instanceof Tx ? tx.data.getNonEmptyNullifiers() : tx.txEffect.nullifiers;
42
39
 
43
40
  // Ditch this tx if it has repeated nullifiers
44
41
  const uniqueNullifiers = new Set(nullifiers);
@@ -49,16 +46,17 @@ export class DoubleSpendTxValidator<T extends AnyTx> implements TxValidator<T> {
49
46
 
50
47
  if (this.isValidatingBlock) {
51
48
  for (const nullifier of nullifiers) {
52
- if (thisBlockNullifiers.has(nullifier)) {
49
+ const nullifierBigInt = nullifier.toBigInt();
50
+ if (thisBlockNullifiers.has(nullifierBigInt)) {
53
51
  this.#log.warn(`Rejecting tx ${Tx.getHash(tx)} for repeating a nullifier in the same block`);
54
52
  return false;
55
53
  }
56
54
 
57
- thisBlockNullifiers.add(nullifier);
55
+ thisBlockNullifiers.add(nullifierBigInt);
58
56
  }
59
57
  }
60
58
 
61
- const nullifierIndexes = await Promise.all(nullifiers.map(n => this.#nullifierSource.getNullifierIndex(new Fr(n))));
59
+ const nullifierIndexes = await this.#nullifierSource.getNullifierIndices(nullifiers.map(n => n.toBuffer()));
62
60
 
63
61
  const hasDuplicates = nullifierIndexes.some(index => index !== undefined);
64
62
  if (hasDuplicates) {
@@ -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
 
@@ -1 +0,0 @@
1
- {"version":3,"file":"data_store.d.ts","sourceRoot":"","sources":["../../src/service/data_store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAY,MAAM,iBAAiB,CAAC;AAE9D,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,SAAS,EAAE,GAAG,EAAE,KAAK,QAAQ,EAAE,KAAK,IAAI,EAAE,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5G,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAqBrD,qBAAa,cAAe,YAAW,SAAS;;IAM9C,OAAO,CAAC,cAAc,CAAS;gBAEnB,EAAE,EAAE,YAAY,EAAE,EAAE,cAAc,EAAE;;KAAyB;IAOzE,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO;IAItB,GAAG,CAAC,GAAG,EAAE,GAAG,GAAG,UAAU;IAgBzB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC;IAIrC,OAAO,CAAC,MAAM,EAAE,aAAa,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC;IAOxD,OAAO,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC;IASxD,UAAU,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC;IAO3D,MAAM,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrC,KAAK,IAAI,KAAK;IA4Bd,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC;IA4BpC,SAAS,CAAC,CAAC,EAAE,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC;YA2B5B,IAAI;YAqBH,GAAG;IAkBlB;;OAEG;IACH,OAAO,CAAC,oBAAoB;CAiB7B"}