@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.
- package/dest/bootstrap/bootstrap.d.ts +4 -1
- package/dest/bootstrap/bootstrap.d.ts.map +1 -1
- package/dest/bootstrap/bootstrap.js +21 -9
- package/dest/client/index.d.ts +5 -4
- package/dest/client/index.d.ts.map +1 -1
- package/dest/client/index.js +18 -12
- package/dest/client/p2p_client.d.ts +13 -20
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +32 -15
- package/dest/config.d.ts +1 -1
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +2 -2
- package/dest/errors/reqresp.error.d.ts +12 -1
- package/dest/errors/reqresp.error.d.ts.map +1 -1
- package/dest/errors/reqresp.error.js +15 -2
- package/dest/index.d.ts +1 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +2 -2
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +9 -0
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +3 -0
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -0
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +171 -0
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +29 -0
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -0
- package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +114 -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 +3 -3
- package/dest/mem_pools/instrumentation.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.js +2 -20
- package/dest/mem_pools/interface.d.ts +4 -3
- package/dest/mem_pools/interface.d.ts.map +1 -1
- 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 +4 -4
- 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 +6 -6
- package/dest/mocks/index.d.ts.map +1 -1
- package/dest/mocks/index.js +9 -9
- package/dest/services/data_store.d.ts.map +1 -0
- package/dest/services/data_store.js +188 -0
- package/dest/{service → services/discv5}/discV5_service.d.ts +4 -2
- package/dest/services/discv5/discV5_service.d.ts.map +1 -0
- package/dest/services/discv5/discV5_service.js +144 -0
- package/dest/{service → services}/dummy_service.d.ts +3 -1
- package/dest/services/dummy_service.d.ts.map +1 -0
- package/dest/{service → services}/dummy_service.js +5 -1
- package/dest/{service → services}/encoding.d.ts +5 -0
- package/dest/services/encoding.d.ts.map +1 -0
- package/dest/services/encoding.js +65 -0
- package/dest/services/index.d.ts +3 -0
- package/dest/services/index.d.ts.map +1 -0
- package/dest/services/index.js +3 -0
- package/dest/{service → services/libp2p}/libp2p_service.d.ts +50 -11
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -0
- package/dest/services/libp2p/libp2p_service.js +573 -0
- package/dest/{service → services/peer-scoring}/peer_scoring.d.ts +5 -2
- package/dest/services/peer-scoring/peer_scoring.d.ts.map +1 -0
- package/dest/services/peer-scoring/peer_scoring.js +72 -0
- package/dest/{service → services}/peer_manager.d.ts +8 -3
- package/dest/services/peer_manager.d.ts.map +1 -0
- package/dest/services/peer_manager.js +230 -0
- package/dest/services/reqresp/config.d.ts.map +1 -0
- package/dest/{service → services}/reqresp/config.js +1 -1
- package/dest/services/reqresp/handlers.d.ts.map +1 -0
- package/dest/{service → services}/reqresp/handlers.js +1 -1
- package/dest/services/reqresp/index.d.ts.map +1 -0
- package/dest/{service → services}/reqresp/index.js +1 -1
- package/dest/services/reqresp/interface.d.ts.map +1 -0
- package/dest/{service → services}/reqresp/interface.js +1 -1
- package/dest/services/reqresp/rate_limiter/index.d.ts.map +1 -0
- package/dest/{service → services}/reqresp/rate_limiter/index.js +1 -1
- package/dest/services/reqresp/rate_limiter/rate_limiter.d.ts.map +1 -0
- package/dest/{service → services}/reqresp/rate_limiter/rate_limiter.js +2 -2
- package/dest/services/reqresp/rate_limiter/rate_limits.d.ts.map +1 -0
- package/dest/{service → services}/reqresp/rate_limiter/rate_limits.js +1 -1
- package/dest/{service → services}/reqresp/reqresp.d.ts +16 -0
- package/dest/services/reqresp/reqresp.d.ts.map +1 -0
- package/dest/{service → services}/reqresp/reqresp.js +69 -20
- package/dest/{service → services}/service.d.ts +2 -1
- package/dest/services/service.d.ts.map +1 -0
- package/dest/{service → services}/service.js +1 -1
- package/dest/tx_validator/aggregate_tx_validator.d.ts +1 -1
- package/dest/tx_validator/aggregate_tx_validator.d.ts.map +1 -1
- package/dest/tx_validator/aggregate_tx_validator.js +5 -3
- package/dest/tx_validator/data_validator.js +3 -3
- package/dest/tx_validator/double_spend_validator.d.ts +3 -2
- package/dest/tx_validator/double_spend_validator.d.ts.map +1 -1
- package/dest/tx_validator/double_spend_validator.js +8 -8
- package/dest/tx_validator/metadata_validator.js +3 -3
- package/dest/tx_validator/tx_proof_validator.js +3 -3
- package/package.json +12 -8
- package/src/bootstrap/bootstrap.ts +24 -10
- package/src/client/index.ts +44 -19
- package/src/client/p2p_client.ts +58 -36
- package/src/config.ts +1 -1
- package/src/errors/reqresp.error.ts +15 -1
- package/src/index.ts +1 -1
- package/src/mem_pools/attestation_pool/attestation_pool.ts +10 -0
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +237 -0
- package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +153 -0
- package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +2 -2
- package/src/mem_pools/instrumentation.ts +1 -21
- package/src/mem_pools/interface.ts +5 -3
- package/src/mem_pools/tx_pool/aztec_kv_tx_pool.ts +3 -3
- package/src/mem_pools/tx_pool/memory_tx_pool.ts +3 -3
- package/src/mocks/index.ts +14 -11
- package/src/{service → services/discv5}/discV5_service.ts +20 -15
- package/src/{service → services}/dummy_service.ts +6 -1
- package/src/{service → services}/encoding.ts +21 -3
- package/src/services/index.ts +2 -0
- package/src/{service → services/libp2p}/libp2p_service.ts +208 -96
- package/src/{service → services/peer-scoring}/peer_scoring.ts +9 -2
- package/src/{service → services}/peer_manager.ts +73 -24
- package/src/{service → services}/reqresp/rate_limiter/rate_limiter.ts +1 -1
- package/src/{service → services}/reqresp/reqresp.ts +87 -21
- package/src/{service → services}/service.ts +3 -1
- package/src/tx_validator/aggregate_tx_validator.ts +5 -3
- package/src/tx_validator/data_validator.ts +2 -2
- package/src/tx_validator/double_spend_validator.ts +8 -10
- package/src/tx_validator/metadata_validator.ts +2 -2
- package/src/tx_validator/tx_proof_validator.ts +2 -2
- package/dest/service/data_store.d.ts.map +0 -1
- package/dest/service/data_store.js +0 -188
- package/dest/service/discV5_service.d.ts.map +0 -1
- package/dest/service/discV5_service.js +0 -141
- package/dest/service/dummy_service.d.ts.map +0 -1
- package/dest/service/encoding.d.ts.map +0 -1
- package/dest/service/encoding.js +0 -49
- package/dest/service/index.d.ts +0 -3
- package/dest/service/index.d.ts.map +0 -1
- package/dest/service/index.js +0 -3
- package/dest/service/libp2p_service.d.ts.map +0 -1
- package/dest/service/libp2p_service.js +0 -496
- package/dest/service/peer_manager.d.ts.map +0 -1
- package/dest/service/peer_manager.js +0 -176
- package/dest/service/peer_scoring.d.ts.map +0 -1
- package/dest/service/peer_scoring.js +0 -67
- package/dest/service/reqresp/config.d.ts.map +0 -1
- package/dest/service/reqresp/handlers.d.ts.map +0 -1
- package/dest/service/reqresp/index.d.ts.map +0 -1
- package/dest/service/reqresp/interface.d.ts.map +0 -1
- package/dest/service/reqresp/rate_limiter/index.d.ts.map +0 -1
- package/dest/service/reqresp/rate_limiter/rate_limiter.d.ts.map +0 -1
- package/dest/service/reqresp/rate_limiter/rate_limits.d.ts.map +0 -1
- package/dest/service/reqresp/reqresp.d.ts.map +0 -1
- package/dest/service/service.d.ts.map +0 -1
- package/src/service/index.ts +0 -2
- /package/dest/{service → services}/data_store.d.ts +0 -0
- /package/dest/{service → services}/reqresp/config.d.ts +0 -0
- /package/dest/{service → services}/reqresp/handlers.d.ts +0 -0
- /package/dest/{service → services}/reqresp/index.d.ts +0 -0
- /package/dest/{service → services}/reqresp/interface.d.ts +0 -0
- /package/dest/{service → services}/reqresp/rate_limiter/index.d.ts +0 -0
- /package/dest/{service → services}/reqresp/rate_limiter/rate_limiter.d.ts +0 -0
- /package/dest/{service → services}/reqresp/rate_limiter/rate_limits.d.ts +0 -0
- /package/src/{service → services}/data_store.ts +0 -0
- /package/src/{service → services}/reqresp/config.ts +0 -0
- /package/src/{service → services}/reqresp/handlers.ts +0 -0
- /package/src/{service → services}/reqresp/index.ts +0 -0
- /package/src/{service → services}/reqresp/interface.ts +0 -0
- /package/src/{service → services}/reqresp/rate_limiter/index.ts +0 -0
- /package/src/{service → services}/reqresp/rate_limiter/rate_limits.ts +0 -0
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
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.
|
|
42
|
+
this.logger.verbose(`Connected to bootstrap peer ${peerId.toString()}`);
|
|
38
43
|
} else {
|
|
39
|
-
this.logger.
|
|
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.
|
|
52
|
+
this.logger.verbose(`Disconnected from bootstrap peer ${peerId.toString()}`);
|
|
48
53
|
} else {
|
|
49
|
-
this.logger.
|
|
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.
|
|
85
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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,
|
|
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 {
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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 =
|
|
239
|
-
return
|
|
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(
|
|
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 {
|
|
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,12 @@
|
|
|
1
1
|
import { type AnyTx, Tx, type TxValidator } from '@aztec/circuit-types';
|
|
2
|
-
import {
|
|
3
|
-
import { createDebugLogger } from '@aztec/foundation/log';
|
|
2
|
+
import { createLogger } from '@aztec/foundation/log';
|
|
4
3
|
|
|
5
4
|
export interface NullifierSource {
|
|
6
|
-
|
|
5
|
+
getNullifierIndices: (nullifiers: Buffer[]) => Promise<(bigint | undefined)[]>;
|
|
7
6
|
}
|
|
8
7
|
|
|
9
8
|
export class DoubleSpendTxValidator<T extends AnyTx> implements TxValidator<T> {
|
|
10
|
-
#log =
|
|
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 =
|
|
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
|
-
|
|
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(
|
|
55
|
+
thisBlockNullifiers.add(nullifierBigInt);
|
|
58
56
|
}
|
|
59
57
|
}
|
|
60
58
|
|
|
61
|
-
const nullifierIndexes = await
|
|
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 {
|
|
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
|
|
|
@@ -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"}
|