@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.
- package/dest/bootstrap/bootstrap.d.ts +7 -2
- package/dest/bootstrap/bootstrap.d.ts.map +1 -1
- package/dest/bootstrap/bootstrap.js +25 -12
- package/dest/client/index.d.ts.map +1 -1
- package/dest/client/index.js +11 -7
- package/dest/client/p2p_client.d.ts +11 -16
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +32 -8
- package/dest/config.d.ts +40 -2
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +10 -2
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +8 -0
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +1 -0
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +22 -3
- package/dest/mem_pools/instrumentation.d.ts +2 -7
- package/dest/mem_pools/instrumentation.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.js +4 -25
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/aztec_kv_tx_pool.js +5 -7
- package/dest/mem_pools/tx_pool/memory_tx_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool/memory_tx_pool.js +4 -4
- package/dest/mocks/index.d.ts.map +1 -1
- package/dest/mocks/index.js +12 -6
- package/dest/service/discV5_service.d.ts +2 -0
- package/dest/service/discV5_service.d.ts.map +1 -1
- package/dest/service/discV5_service.js +14 -11
- package/dest/service/dummy_service.d.ts +3 -1
- package/dest/service/dummy_service.d.ts.map +1 -1
- package/dest/service/dummy_service.js +5 -1
- package/dest/service/encoding.d.ts +26 -0
- package/dest/service/encoding.d.ts.map +1 -0
- package/dest/service/encoding.js +49 -0
- package/dest/service/libp2p_service.d.ts +2 -7
- package/dest/service/libp2p_service.d.ts.map +1 -1
- package/dest/service/libp2p_service.js +24 -31
- package/dest/service/peer_manager.d.ts +3 -0
- package/dest/service/peer_manager.d.ts.map +1 -1
- package/dest/service/peer_manager.js +59 -21
- package/dest/service/peer_scoring.d.ts +4 -1
- package/dest/service/peer_scoring.d.ts.map +1 -1
- package/dest/service/peer_scoring.js +6 -1
- package/dest/service/reqresp/handlers.d.ts +4 -2
- package/dest/service/reqresp/handlers.d.ts.map +1 -1
- package/dest/service/reqresp/handlers.js +3 -3
- package/dest/service/reqresp/interface.d.ts +1 -1
- package/dest/service/reqresp/interface.d.ts.map +1 -1
- package/dest/service/reqresp/interface.js +2 -2
- package/dest/service/reqresp/reqresp.d.ts +3 -0
- package/dest/service/reqresp/reqresp.d.ts.map +1 -1
- package/dest/service/reqresp/reqresp.js +14 -9
- package/dest/service/service.d.ts +2 -1
- package/dest/service/service.d.ts.map +1 -1
- package/dest/tx_validator/data_validator.js +3 -3
- package/dest/tx_validator/double_spend_validator.js +3 -3
- package/dest/tx_validator/metadata_validator.js +3 -3
- package/dest/tx_validator/tx_proof_validator.js +3 -3
- package/dest/util.d.ts +20 -2
- package/dest/util.d.ts.map +1 -1
- package/dest/util.js +39 -3
- package/package.json +14 -9
- package/src/bootstrap/bootstrap.ts +33 -13
- package/src/client/index.ts +10 -6
- package/src/client/p2p_client.ts +47 -24
- package/src/config.ts +17 -2
- package/src/mem_pools/attestation_pool/attestation_pool.ts +9 -0
- package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +23 -2
- package/src/mem_pools/instrumentation.ts +4 -26
- package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +4 -6
- package/src/mem_pools/tx_pool/memory_tx_pool.ts +3 -3
- package/src/mocks/index.ts +11 -5
- package/src/service/discV5_service.ts +17 -12
- package/src/service/dummy_service.ts +6 -1
- package/src/service/encoding.ts +61 -0
- package/src/service/libp2p_service.ts +26 -32
- package/src/service/peer_manager.ts +68 -22
- package/src/service/peer_scoring.ts +8 -1
- package/src/service/reqresp/handlers.ts +4 -4
- package/src/service/reqresp/interface.ts +3 -3
- package/src/service/reqresp/reqresp.ts +13 -8
- package/src/service/service.ts +3 -1
- package/src/tx_validator/data_validator.ts +2 -2
- package/src/tx_validator/double_spend_validator.ts +2 -2
- package/src/tx_validator/metadata_validator.ts +2 -2
- package/src/tx_validator/tx_proof_validator.ts +2 -2
- package/src/util.ts +48 -2
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {
|
|
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 =
|
|
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.
|
|
40
|
+
this.logger.verbose(`Connected to bootstrap peer ${peerId.toString()}`);
|
|
38
41
|
} else {
|
|
39
|
-
this.logger.
|
|
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.
|
|
50
|
+
this.logger.verbose(`Disconnected from bootstrap peer ${peerId.toString()}`);
|
|
48
51
|
} else {
|
|
49
|
-
this.logger.
|
|
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.
|
|
85
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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):
|
|
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
|
}
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* @param _msg - The ping request message.
|
|
4
4
|
* @returns A resolved promise with the pong response.
|
|
5
5
|
*/
|
|
6
|
-
export function pingHandler(_msg: any): Promise<
|
|
7
|
-
return Promise.resolve(
|
|
6
|
+
export function pingHandler(_msg: any): Promise<Buffer> {
|
|
7
|
+
return Promise.resolve(Buffer.from('pong'));
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -12,6 +12,6 @@ export function pingHandler(_msg: any): Promise<Uint8Array> {
|
|
|
12
12
|
* @param _msg - The status request message.
|
|
13
13
|
* @returns A resolved promise with the ok response.
|
|
14
14
|
*/
|
|
15
|
-
export function statusHandler(_msg: any): Promise<
|
|
16
|
-
return Promise.resolve(
|
|
15
|
+
export function statusHandler(_msg: any): Promise<Buffer> {
|
|
16
|
+
return Promise.resolve(Buffer.from('ok'));
|
|
17
17
|
}
|
|
@@ -16,7 +16,7 @@ export type ReqRespSubProtocol = typeof PING_PROTOCOL | typeof STATUS_PROTOCOL |
|
|
|
16
16
|
* A handler for a sub protocol
|
|
17
17
|
* The message will arrive as a buffer, and the handler must return a buffer
|
|
18
18
|
*/
|
|
19
|
-
export type ReqRespSubProtocolHandler = (msg: Buffer) => Promise<
|
|
19
|
+
export type ReqRespSubProtocolHandler = (msg: Buffer) => Promise<Buffer>;
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* A type mapping from supprotocol to it's rate limits
|
|
@@ -83,8 +83,8 @@ export type SubProtocolMap = {
|
|
|
83
83
|
* Default handler for unimplemented sub protocols, this SHOULD be overwritten
|
|
84
84
|
* by the service, but is provided as a fallback
|
|
85
85
|
*/
|
|
86
|
-
const defaultHandler = (_msg: any): Promise<
|
|
87
|
-
return Promise.resolve(
|
|
86
|
+
const defaultHandler = (_msg: any): Promise<Buffer> => {
|
|
87
|
+
return Promise.resolve(Buffer.from('unimplemented'));
|
|
88
88
|
};
|
|
89
89
|
|
|
90
90
|
/**
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// @attribution: lodestar impl for inspiration
|
|
2
|
-
import { type Logger,
|
|
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';
|
|
8
9
|
import { type Uint8ArrayList } from 'uint8arraylist';
|
|
9
10
|
|
|
10
11
|
import { CollectiveReqRespTimeoutError, IndiviualReqRespTimeoutError } from '../../errors/reqresp.error.js';
|
|
@@ -31,6 +32,9 @@ import { RequestResponseRateLimiter } from './rate_limiter/rate_limiter.js';
|
|
|
31
32
|
* This service implements the request response sub protocol, it is heavily inspired from
|
|
32
33
|
* ethereum implementations of the same name.
|
|
33
34
|
*
|
|
35
|
+
* Note, responses get compressed in streamHandler
|
|
36
|
+
* so they get decompressed in readMessage
|
|
37
|
+
*
|
|
34
38
|
* see: https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/p2p-interface.md#the-reqresp-domain
|
|
35
39
|
*/
|
|
36
40
|
export class ReqResp {
|
|
@@ -46,7 +50,7 @@ export class ReqResp {
|
|
|
46
50
|
private rateLimiter: RequestResponseRateLimiter;
|
|
47
51
|
|
|
48
52
|
constructor(config: P2PReqRespConfig, protected readonly libp2p: Libp2p, private peerManager: PeerManager) {
|
|
49
|
-
this.logger =
|
|
53
|
+
this.logger = createLogger('p2p:reqresp');
|
|
50
54
|
|
|
51
55
|
this.overallRequestTimeoutMs = config.overallRequestTimeoutMs;
|
|
52
56
|
this.individualRequestTimeoutMs = config.individualRequestTimeoutMs;
|
|
@@ -192,7 +196,7 @@ export class ReqResp {
|
|
|
192
196
|
let stream: Stream | undefined;
|
|
193
197
|
try {
|
|
194
198
|
stream = await this.libp2p.dialProtocol(peerId, subProtocol);
|
|
195
|
-
this.logger.
|
|
199
|
+
this.logger.trace(`Stream opened with ${peerId.toString()} for ${subProtocol}`);
|
|
196
200
|
|
|
197
201
|
// Open the stream with a timeout
|
|
198
202
|
const result = await executeTimeoutWithCustomError<Buffer>(
|
|
@@ -202,17 +206,17 @@ export class ReqResp {
|
|
|
202
206
|
);
|
|
203
207
|
|
|
204
208
|
await stream.close();
|
|
205
|
-
this.logger.
|
|
209
|
+
this.logger.trace(`Stream closed with ${peerId.toString()} for ${subProtocol}`);
|
|
206
210
|
|
|
207
211
|
return result;
|
|
208
212
|
} catch (e: any) {
|
|
209
|
-
this.logger.error(
|
|
213
|
+
this.logger.error(`Error sending request to peer`, e, { peerId: peerId.toString(), subProtocol });
|
|
210
214
|
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.HighToleranceError);
|
|
211
215
|
} finally {
|
|
212
216
|
if (stream) {
|
|
213
217
|
try {
|
|
214
218
|
await stream.close();
|
|
215
|
-
this.logger.
|
|
219
|
+
this.logger.trace(`Stream closed with ${peerId.toString()} for ${subProtocol}`);
|
|
216
220
|
} catch (closeError) {
|
|
217
221
|
this.logger.error(
|
|
218
222
|
`Error closing stream: ${closeError instanceof Error ? closeError.message : 'Unknown error'}`,
|
|
@@ -232,7 +236,7 @@ export class ReqResp {
|
|
|
232
236
|
chunks.push(chunk.subarray());
|
|
233
237
|
}
|
|
234
238
|
const messageData = chunks.concat();
|
|
235
|
-
return Buffer.concat(messageData);
|
|
239
|
+
return uncompressSync(Buffer.concat(messageData), { asBuffer: true }) as Buffer;
|
|
236
240
|
}
|
|
237
241
|
|
|
238
242
|
/**
|
|
@@ -269,7 +273,8 @@ export class ReqResp {
|
|
|
269
273
|
async function* (source: any) {
|
|
270
274
|
for await (const chunkList of source) {
|
|
271
275
|
const msg = Buffer.from(chunkList.subarray());
|
|
272
|
-
|
|
276
|
+
const response = await handler(msg);
|
|
277
|
+
yield new Uint8Array(compressSync(response));
|
|
273
278
|
}
|
|
274
279
|
},
|
|
275
280
|
stream,
|
package/src/service/service.ts
CHANGED
|
@@ -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 {
|
|
2
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
3
3
|
|
|
4
4
|
export class DataTxValidator implements TxValidator<Tx> {
|
|
5
|
-
#log =
|
|
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 {
|
|
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 =
|
|
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 {
|
|
3
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
4
4
|
|
|
5
5
|
export class MetadataTxValidator<T extends AnyTx> implements TxValidator<T> {
|
|
6
|
-
#log =
|
|
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 {
|
|
2
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
3
3
|
|
|
4
4
|
export class TxProofValidator implements TxValidator<Tx> {
|
|
5
|
-
#log =
|
|
5
|
+
#log = createLogger('p2p:tx_validator:private_proof');
|
|
6
6
|
|
|
7
7
|
constructor(private verifier: ClientProtocolCircuitVerifier) {}
|
|
8
8
|
|
package/src/util.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { type AztecKVStore, type AztecSingleton } from '@aztec/kv-store';
|
|
1
2
|
import { type DataStoreConfig } from '@aztec/kv-store/config';
|
|
2
3
|
|
|
3
4
|
import type { GossipSub } from '@chainsafe/libp2p-gossipsub';
|
|
5
|
+
import { generateKeyPair, marshalPrivateKey, unmarshalPrivateKey } from '@libp2p/crypto/keys';
|
|
6
|
+
import { type PeerId, type PrivateKey } from '@libp2p/interface';
|
|
7
|
+
import { createFromPrivKey } from '@libp2p/peer-id-factory';
|
|
4
8
|
import { resolve } from 'dns/promises';
|
|
5
9
|
import type { Libp2p } from 'libp2p';
|
|
6
10
|
|
|
@@ -19,8 +23,7 @@ export interface PubSubLibp2p extends Libp2p {
|
|
|
19
23
|
* const udpAddr = '[2001:db8::1]:8080' -> /ip6/2001:db8::1/udp/8080
|
|
20
24
|
* @param address - The address string to convert. Has to be in the format <addr>:<port>.
|
|
21
25
|
* @param protocol - The protocol to use in the multiaddr string.
|
|
22
|
-
* @returns A multiaddr compliant string.
|
|
23
|
-
*/
|
|
26
|
+
* @returns A multiaddr compliant string. */
|
|
24
27
|
export function convertToMultiaddr(address: string, protocol: 'tcp' | 'udp'): string {
|
|
25
28
|
const [addr, port] = splitAddressPort(address, false);
|
|
26
29
|
|
|
@@ -141,3 +144,46 @@ export async function configureP2PClientAddresses(
|
|
|
141
144
|
|
|
142
145
|
return config;
|
|
143
146
|
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get the peer id private key
|
|
150
|
+
*
|
|
151
|
+
* 1. Check if we have a peer id private key in the config
|
|
152
|
+
* 2. If not, check we have a peer id private key persisted in the node
|
|
153
|
+
* 3. If not, create a new one, then persist it in the node
|
|
154
|
+
*
|
|
155
|
+
*/
|
|
156
|
+
export async function getPeerIdPrivateKey(config: { peerIdPrivateKey?: string }, store: AztecKVStore): Promise<string> {
|
|
157
|
+
const peerIdPrivateKeySingleton: AztecSingleton<string> = store.openSingleton('peerIdPrivateKey');
|
|
158
|
+
if (config.peerIdPrivateKey) {
|
|
159
|
+
await peerIdPrivateKeySingleton.set(config.peerIdPrivateKey);
|
|
160
|
+
return config.peerIdPrivateKey;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const storedPeerIdPrivateKey = peerIdPrivateKeySingleton.get();
|
|
164
|
+
if (storedPeerIdPrivateKey) {
|
|
165
|
+
return storedPeerIdPrivateKey;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const newPeerIdPrivateKey = await generateKeyPair('secp256k1');
|
|
169
|
+
const privateKeyString = Buffer.from(marshalPrivateKey(newPeerIdPrivateKey)).toString('hex');
|
|
170
|
+
|
|
171
|
+
await peerIdPrivateKeySingleton.set(privateKeyString);
|
|
172
|
+
return privateKeyString;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Create a libp2p peer ID from the private key.
|
|
177
|
+
* @param privateKey - peer ID private key as hex string
|
|
178
|
+
* @returns The peer ID.
|
|
179
|
+
*/
|
|
180
|
+
export async function createLibP2PPeerIdFromPrivateKey(privateKey: string): Promise<PeerId> {
|
|
181
|
+
if (!privateKey?.length) {
|
|
182
|
+
throw new Error('No peer private key provided');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const asLibp2pPrivateKey: PrivateKey<'secp256k1'> = await unmarshalPrivateKey(
|
|
186
|
+
new Uint8Array(Buffer.from(privateKey, 'hex')),
|
|
187
|
+
);
|
|
188
|
+
return await createFromPrivKey(asLibp2pPrivateKey);
|
|
189
|
+
}
|