@aztec/p2p 0.67.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.js +2 -2
- package/dest/client/index.d.ts +5 -4
- package/dest/client/index.d.ts.map +1 -1
- package/dest/client/index.js +12 -9
- package/dest/client/p2p_client.d.ts +6 -6
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +12 -11
- 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/interface.d.ts +4 -3
- package/dest/mem_pools/interface.d.ts.map +1 -1
- package/dest/mocks/index.d.ts +6 -6
- package/dest/mocks/index.d.ts.map +1 -1
- package/dest/mocks/index.js +8 -8
- 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 +2 -2
- package/dest/services/discv5/discV5_service.d.ts.map +1 -0
- package/dest/services/discv5/discV5_service.js +144 -0
- package/dest/services/dummy_service.d.ts.map +1 -0
- package/dest/{service → services}/dummy_service.js +1 -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 +48 -10
- 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 +1 -1
- 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 +5 -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/services/reqresp/reqresp.js +279 -0
- 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/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 +6 -6
- package/package.json +8 -7
- package/src/bootstrap/bootstrap.ts +1 -1
- package/src/client/index.ts +38 -16
- package/src/client/p2p_client.ts +28 -15
- 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/interface.ts +5 -3
- package/src/mocks/index.ts +13 -10
- package/src/{service → services/discv5}/discV5_service.ts +3 -3
- package/src/{service → services}/encoding.ts +21 -3
- package/src/services/index.ts +2 -0
- package/src/{service → services/libp2p}/libp2p_service.ts +192 -86
- package/src/{service → services/peer-scoring}/peer_scoring.ts +1 -1
- package/src/{service → services}/peer_manager.ts +5 -2
- package/src/{service → services}/reqresp/rate_limiter/rate_limiter.ts +1 -1
- package/src/{service → services}/reqresp/reqresp.ts +83 -17
- package/src/tx_validator/aggregate_tx_validator.ts +5 -3
- package/src/tx_validator/double_spend_validator.ts +6 -8
- 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 -144
- 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 -500
- package/dest/service/peer_manager.d.ts.map +0 -1
- package/dest/service/peer_manager.js +0 -214
- package/dest/service/peer_scoring.d.ts.map +0 -1
- package/dest/service/peer_scoring.js +0 -72
- 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/reqresp/reqresp.js +0 -230
- 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}/dummy_service.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/dest/{service → services}/service.d.ts +0 -0
- /package/src/{service → services}/data_store.ts +0 -0
- /package/src/{service → services}/dummy_service.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
- /package/src/{service → services}/service.ts +0 -0
|
@@ -49,13 +49,31 @@ export function getMsgIdFn(message: Message) {
|
|
|
49
49
|
return sha256(Buffer.concat(vec)).subarray(0, 20);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Snappy transform for libp2p gossipsub
|
|
54
|
+
*/
|
|
52
55
|
export class SnappyTransform implements DataTransform {
|
|
56
|
+
// Topic string included to satisfy DataTransform interface
|
|
53
57
|
inboundTransform(_topicStr: string, data: Uint8Array): Uint8Array {
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
return this.inboundTransformNoTopic(Buffer.from(data));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public inboundTransformNoTopic(data: Buffer): Buffer {
|
|
62
|
+
if (data.length === 0) {
|
|
63
|
+
return data;
|
|
64
|
+
}
|
|
65
|
+
return Buffer.from(uncompressSync(data, { asBuffer: true }));
|
|
56
66
|
}
|
|
57
67
|
|
|
68
|
+
// Topic string included to satisfy DataTransform interface
|
|
58
69
|
outboundTransform(_topicStr: string, data: Uint8Array): Uint8Array {
|
|
59
|
-
return
|
|
70
|
+
return this.outboundTransformNoTopic(Buffer.from(data));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public outboundTransformNoTopic(data: Buffer): Buffer {
|
|
74
|
+
if (data.length === 0) {
|
|
75
|
+
return data;
|
|
76
|
+
}
|
|
77
|
+
return Buffer.from(compressSync(data));
|
|
60
78
|
}
|
|
61
79
|
}
|
|
@@ -8,13 +8,14 @@ import {
|
|
|
8
8
|
MerkleTreeId,
|
|
9
9
|
type PeerInfo,
|
|
10
10
|
type RawGossipMessage,
|
|
11
|
-
TopicType,
|
|
12
11
|
TopicTypeMap,
|
|
13
12
|
Tx,
|
|
14
13
|
TxHash,
|
|
15
14
|
type WorldStateSynchronizer,
|
|
15
|
+
getTopicTypeForClientType,
|
|
16
16
|
metricsTopicStrToLabels,
|
|
17
17
|
} from '@aztec/circuit-types';
|
|
18
|
+
import { P2PClientType } from '@aztec/circuit-types';
|
|
18
19
|
import { Fr } from '@aztec/circuits.js';
|
|
19
20
|
import { createLogger } from '@aztec/foundation/log';
|
|
20
21
|
import { SerialQueue } from '@aztec/foundation/queue';
|
|
@@ -28,26 +29,26 @@ import { createPeerScoreParams, createTopicScoreParams } from '@chainsafe/libp2p
|
|
|
28
29
|
import { noise } from '@chainsafe/libp2p-noise';
|
|
29
30
|
import { yamux } from '@chainsafe/libp2p-yamux';
|
|
30
31
|
import { identify } from '@libp2p/identify';
|
|
31
|
-
import type
|
|
32
|
+
import { type Message, type PeerId, TopicValidatorResult } from '@libp2p/interface';
|
|
32
33
|
import '@libp2p/kad-dht';
|
|
33
34
|
import { mplex } from '@libp2p/mplex';
|
|
34
35
|
import { tcp } from '@libp2p/tcp';
|
|
35
36
|
import { createLibp2p } from 'libp2p';
|
|
36
37
|
|
|
37
|
-
import { type P2PConfig } from '
|
|
38
|
-
import { type MemPools } from '
|
|
38
|
+
import { type P2PConfig } from '../../config.js';
|
|
39
|
+
import { type MemPools } from '../../mem_pools/interface.js';
|
|
39
40
|
import {
|
|
40
41
|
DataTxValidator,
|
|
41
42
|
DoubleSpendTxValidator,
|
|
42
43
|
MetadataTxValidator,
|
|
43
44
|
TxProofValidator,
|
|
44
|
-
} from '
|
|
45
|
-
import { type PubSubLibp2p, convertToMultiaddr } from '
|
|
46
|
-
import { AztecDatastore } from '
|
|
47
|
-
import { SnappyTransform, fastMsgIdFn, getMsgIdFn, msgIdToStrFn } from '
|
|
48
|
-
import {
|
|
49
|
-
import {
|
|
50
|
-
import { pingHandler, statusHandler } from '
|
|
45
|
+
} from '../../tx_validator/index.js';
|
|
46
|
+
import { type PubSubLibp2p, convertToMultiaddr } from '../../util.js';
|
|
47
|
+
import { AztecDatastore } from '../data_store.js';
|
|
48
|
+
import { SnappyTransform, fastMsgIdFn, getMsgIdFn, msgIdToStrFn } from '../encoding.js';
|
|
49
|
+
import { PeerErrorSeverity } from '../peer-scoring/peer_scoring.js';
|
|
50
|
+
import { PeerManager } from '../peer_manager.js';
|
|
51
|
+
import { pingHandler, statusHandler } from '../reqresp/handlers.js';
|
|
51
52
|
import {
|
|
52
53
|
DEFAULT_SUB_PROTOCOL_HANDLERS,
|
|
53
54
|
DEFAULT_SUB_PROTOCOL_VALIDATORS,
|
|
@@ -57,14 +58,29 @@ import {
|
|
|
57
58
|
STATUS_PROTOCOL,
|
|
58
59
|
type SubProtocolMap,
|
|
59
60
|
TX_REQ_PROTOCOL,
|
|
60
|
-
} from '
|
|
61
|
-
import { ReqResp } from '
|
|
62
|
-
import type { P2PService, PeerDiscoveryService } from '
|
|
61
|
+
} from '../reqresp/interface.js';
|
|
62
|
+
import { ReqResp } from '../reqresp/reqresp.js';
|
|
63
|
+
import type { P2PService, PeerDiscoveryService } from '../service.js';
|
|
64
|
+
|
|
65
|
+
interface MessageValidator {
|
|
66
|
+
validator: {
|
|
67
|
+
validateTx(tx: Tx): Promise<boolean>;
|
|
68
|
+
};
|
|
69
|
+
severity: PeerErrorSeverity;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface ValidationResult {
|
|
73
|
+
name: string;
|
|
74
|
+
isValid: boolean;
|
|
75
|
+
severity: PeerErrorSeverity;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
type ValidationOutcome = { allPassed: true } | { allPassed: false; failure: ValidationResult };
|
|
63
79
|
|
|
64
80
|
/**
|
|
65
81
|
* Lib P2P implementation of the P2PService interface.
|
|
66
82
|
*/
|
|
67
|
-
export class LibP2PService extends WithTracer implements P2PService {
|
|
83
|
+
export class LibP2PService<T extends P2PClientType> extends WithTracer implements P2PService {
|
|
68
84
|
private jobQueue: SerialQueue = new SerialQueue();
|
|
69
85
|
private peerManager: PeerManager;
|
|
70
86
|
private discoveryRunningPromise?: RunningPromise;
|
|
@@ -80,10 +96,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
80
96
|
private blockReceivedCallback: (block: BlockProposal) => Promise<BlockAttestation | undefined>;
|
|
81
97
|
|
|
82
98
|
constructor(
|
|
99
|
+
private clientType: T,
|
|
83
100
|
private config: P2PConfig,
|
|
84
101
|
private node: PubSubLibp2p,
|
|
85
102
|
private peerDiscoveryService: PeerDiscoveryService,
|
|
86
|
-
private mempools: MemPools
|
|
103
|
+
private mempools: MemPools<T>,
|
|
87
104
|
private l2BlockSource: L2BlockSource,
|
|
88
105
|
private proofVerifier: ClientProtocolCircuitVerifier,
|
|
89
106
|
private worldStateSynchronizer: WorldStateSynchronizer,
|
|
@@ -93,7 +110,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
93
110
|
) {
|
|
94
111
|
super(telemetry, 'LibP2PService');
|
|
95
112
|
|
|
96
|
-
this.peerManager = new PeerManager(node, peerDiscoveryService, config, logger);
|
|
113
|
+
this.peerManager = new PeerManager(node, peerDiscoveryService, config, this.tracer, logger);
|
|
97
114
|
this.node.services.pubsub.score.params.appSpecificScore = (peerId: string) => {
|
|
98
115
|
return this.peerManager.getPeerScore(peerId);
|
|
99
116
|
};
|
|
@@ -131,22 +148,27 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
131
148
|
await this.node.start();
|
|
132
149
|
|
|
133
150
|
// Subscribe to standard GossipSub topics by default
|
|
134
|
-
for (const topic
|
|
151
|
+
for (const topic of getTopicTypeForClientType(this.clientType)) {
|
|
135
152
|
this.subscribeToTopic(TopicTypeMap[topic].p2pTopic);
|
|
136
153
|
}
|
|
137
154
|
|
|
155
|
+
// Add p2p topic validators
|
|
156
|
+
this.node.services.pubsub.topicValidators.set(Tx.p2pTopic, this.validatePropagatedTxFromMessage.bind(this));
|
|
157
|
+
|
|
138
158
|
// add GossipSub listener
|
|
139
159
|
this.node.services.pubsub.addEventListener('gossipsub:message', async e => {
|
|
140
|
-
const { msg
|
|
160
|
+
const { msg } = e.detail;
|
|
141
161
|
this.logger.trace(`Received PUBSUB message.`);
|
|
142
162
|
|
|
143
|
-
await this.jobQueue.put(() => this.handleNewGossipMessage(msg
|
|
163
|
+
await this.jobQueue.put(() => this.handleNewGossipMessage(msg));
|
|
144
164
|
});
|
|
145
165
|
|
|
146
166
|
// Start running promise for peer discovery
|
|
147
|
-
this.discoveryRunningPromise = new RunningPromise(
|
|
148
|
-
this.peerManager.heartbeat()
|
|
149
|
-
|
|
167
|
+
this.discoveryRunningPromise = new RunningPromise(
|
|
168
|
+
() => this.peerManager.heartbeat(),
|
|
169
|
+
this.logger,
|
|
170
|
+
this.config.peerCheckIntervalMS,
|
|
171
|
+
);
|
|
150
172
|
this.discoveryRunningPromise.start();
|
|
151
173
|
|
|
152
174
|
// Define the sub protocol validators - This is done within this start() method to gain a callback to the existing validateTx function
|
|
@@ -186,11 +208,12 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
186
208
|
* @param txPool - The transaction pool to be accessed by the service.
|
|
187
209
|
* @returns The new service.
|
|
188
210
|
*/
|
|
189
|
-
public static async new(
|
|
211
|
+
public static async new<T extends P2PClientType>(
|
|
212
|
+
clientType: T,
|
|
190
213
|
config: P2PConfig,
|
|
191
214
|
peerDiscoveryService: PeerDiscoveryService,
|
|
192
215
|
peerId: PeerId,
|
|
193
|
-
mempools: MemPools
|
|
216
|
+
mempools: MemPools<T>,
|
|
194
217
|
l2BlockSource: L2BlockSource,
|
|
195
218
|
proofVerifier: ClientProtocolCircuitVerifier,
|
|
196
219
|
worldStateSynchronizer: WorldStateSynchronizer,
|
|
@@ -251,6 +274,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
251
274
|
dataTransform: new SnappyTransform(),
|
|
252
275
|
metricsRegister: otelMetricsAdapter,
|
|
253
276
|
metricsTopicStrToLabel: metricsTopicStrToLabels(),
|
|
277
|
+
asyncValidation: true,
|
|
254
278
|
scoreParams: createPeerScoreParams({
|
|
255
279
|
topics: {
|
|
256
280
|
[Tx.p2pTopic]: createTopicScoreParams({
|
|
@@ -299,6 +323,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
299
323
|
};
|
|
300
324
|
|
|
301
325
|
return new LibP2PService(
|
|
326
|
+
clientType,
|
|
302
327
|
config,
|
|
303
328
|
node,
|
|
304
329
|
peerDiscoveryService,
|
|
@@ -376,12 +401,12 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
376
401
|
* @param topic - The message's topic.
|
|
377
402
|
* @param data - The message data
|
|
378
403
|
*/
|
|
379
|
-
private async handleNewGossipMessage(message: RawGossipMessage
|
|
404
|
+
private async handleNewGossipMessage(message: RawGossipMessage) {
|
|
380
405
|
if (message.topic === Tx.p2pTopic) {
|
|
381
406
|
const tx = Tx.fromBuffer(Buffer.from(message.data));
|
|
382
|
-
await this.processTxFromPeer(tx
|
|
407
|
+
await this.processTxFromPeer(tx);
|
|
383
408
|
}
|
|
384
|
-
if (message.topic === BlockAttestation.p2pTopic) {
|
|
409
|
+
if (message.topic === BlockAttestation.p2pTopic && this.clientType === P2PClientType.Full) {
|
|
385
410
|
const attestation = BlockAttestation.fromBuffer(Buffer.from(message.data));
|
|
386
411
|
await this.processAttestationFromPeer(attestation);
|
|
387
412
|
}
|
|
@@ -410,7 +435,7 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
410
435
|
}))
|
|
411
436
|
private async processAttestationFromPeer(attestation: BlockAttestation): Promise<void> {
|
|
412
437
|
this.logger.debug(`Received attestation ${attestation.p2pMessageIdentifier()} from external peer.`);
|
|
413
|
-
await this.mempools.attestationPool
|
|
438
|
+
await this.mempools.attestationPool!.addAttestations([attestation]);
|
|
414
439
|
}
|
|
415
440
|
|
|
416
441
|
/**Process block from peer
|
|
@@ -468,16 +493,11 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
468
493
|
});
|
|
469
494
|
}
|
|
470
495
|
|
|
471
|
-
private async processTxFromPeer(tx: Tx
|
|
496
|
+
private async processTxFromPeer(tx: Tx): Promise<void> {
|
|
472
497
|
const txHash = tx.getTxHash();
|
|
473
498
|
const txHashString = txHash.toString();
|
|
474
499
|
this.logger.verbose(`Received tx ${txHashString} from external peer.`);
|
|
475
|
-
|
|
476
|
-
const isValidTx = await this.validatePropagatedTx(tx, peerId);
|
|
477
|
-
|
|
478
|
-
if (isValidTx) {
|
|
479
|
-
await this.mempools.txPool.addTxs([tx]);
|
|
480
|
-
}
|
|
500
|
+
await this.mempools.txPool.addTxs([tx]);
|
|
481
501
|
}
|
|
482
502
|
|
|
483
503
|
/**
|
|
@@ -494,6 +514,9 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
494
514
|
* @param peerId - The peer ID of the peer that sent the tx.
|
|
495
515
|
* @returns True if the tx is valid, false otherwise.
|
|
496
516
|
*/
|
|
517
|
+
@trackSpan('Libp2pService.validateRequestedTx', (requestedTxHash, _responseTx) => ({
|
|
518
|
+
[Attributes.TX_HASH]: requestedTxHash.toString(),
|
|
519
|
+
}))
|
|
497
520
|
private async validateRequestedTx(requestedTxHash: TxHash, responseTx: Tx, peerId: PeerId): Promise<boolean> {
|
|
498
521
|
const proofValidator = new TxProofValidator(this.proofVerifier);
|
|
499
522
|
const validProof = await proofValidator.validateTx(responseTx);
|
|
@@ -514,67 +537,150 @@ export class LibP2PService extends WithTracer implements P2PService {
|
|
|
514
537
|
return true;
|
|
515
538
|
}
|
|
516
539
|
|
|
540
|
+
private async validatePropagatedTxFromMessage(
|
|
541
|
+
propagationSource: PeerId,
|
|
542
|
+
msg: Message,
|
|
543
|
+
): Promise<TopicValidatorResult> {
|
|
544
|
+
const tx = Tx.fromBuffer(Buffer.from(msg.data));
|
|
545
|
+
const isValid = await this.validatePropagatedTx(tx, propagationSource);
|
|
546
|
+
this.logger.trace(`validatePropagatedTx: ${isValid}`, {
|
|
547
|
+
[Attributes.TX_HASH]: tx.getTxHash().toString(),
|
|
548
|
+
[Attributes.P2P_ID]: propagationSource.toString(),
|
|
549
|
+
});
|
|
550
|
+
return isValid ? TopicValidatorResult.Accept : TopicValidatorResult.Reject;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Validate a tx that has been propagated from a peer.
|
|
555
|
+
* @param tx - The tx to validate.
|
|
556
|
+
* @param peerId - The peer ID of the peer that sent the tx.
|
|
557
|
+
* @returns True if the tx is valid, false otherwise.
|
|
558
|
+
*/
|
|
559
|
+
@trackSpan('Libp2pService.validatePropagatedTx', tx => ({
|
|
560
|
+
[Attributes.TX_HASH]: tx.getTxHash().toString(),
|
|
561
|
+
}))
|
|
517
562
|
private async validatePropagatedTx(tx: Tx, peerId: PeerId): Promise<boolean> {
|
|
518
563
|
const blockNumber = (await this.l2BlockSource.getBlockNumber()) + 1;
|
|
519
|
-
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
if (
|
|
523
|
-
|
|
524
|
-
this.node.services.pubsub.score.markInvalidMessageDelivery(peerId.toString(), Tx.p2pTopic);
|
|
525
|
-
return false;
|
|
564
|
+
const messageValidators = this.createMessageValidators(blockNumber);
|
|
565
|
+
const outcome = await this.runValidations(tx, messageValidators);
|
|
566
|
+
|
|
567
|
+
if (outcome.allPassed) {
|
|
568
|
+
return true;
|
|
526
569
|
}
|
|
527
570
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
if (
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
571
|
+
const { name, severity } = outcome.failure;
|
|
572
|
+
|
|
573
|
+
// Double spend validator has a special case handler
|
|
574
|
+
if (name === 'doubleSpendValidator') {
|
|
575
|
+
const isValid = await this.handleDoubleSpendFailure(tx, blockNumber, peerId);
|
|
576
|
+
if (isValid) {
|
|
577
|
+
return true;
|
|
578
|
+
}
|
|
535
579
|
}
|
|
536
580
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
581
|
+
this.peerManager.penalizePeer(peerId, severity);
|
|
582
|
+
return false;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Create message validators for the given block number.
|
|
587
|
+
*
|
|
588
|
+
* Each validator is a pair of a validator and a severity.
|
|
589
|
+
* If a validator fails, the peer is penalized with the severity of the validator.
|
|
590
|
+
*
|
|
591
|
+
* @param blockNumber - The block number to create validators for.
|
|
592
|
+
* @returns The message validators.
|
|
593
|
+
*/
|
|
594
|
+
private createMessageValidators(blockNumber: number): Record<string, MessageValidator> {
|
|
595
|
+
return {
|
|
596
|
+
dataValidator: {
|
|
597
|
+
validator: new DataTxValidator(),
|
|
598
|
+
severity: PeerErrorSeverity.HighToleranceError,
|
|
543
599
|
},
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
600
|
+
metadataValidator: {
|
|
601
|
+
validator: new MetadataTxValidator(new Fr(this.config.l1ChainId), new Fr(blockNumber)),
|
|
602
|
+
severity: PeerErrorSeverity.HighToleranceError,
|
|
603
|
+
},
|
|
604
|
+
proofValidator: {
|
|
605
|
+
validator: new TxProofValidator(this.proofVerifier),
|
|
606
|
+
severity: PeerErrorSeverity.MidToleranceError,
|
|
607
|
+
},
|
|
608
|
+
doubleSpendValidator: {
|
|
609
|
+
validator: new DoubleSpendTxValidator({
|
|
610
|
+
getNullifierIndices: (nullifiers: Buffer[]) => {
|
|
611
|
+
const merkleTree = this.worldStateSynchronizer.getCommitted();
|
|
612
|
+
return merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
|
|
556
613
|
},
|
|
557
|
-
})
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
614
|
+
}),
|
|
615
|
+
severity: PeerErrorSeverity.HighToleranceError,
|
|
616
|
+
},
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Run validations on a tx.
|
|
622
|
+
* @param tx - The tx to validate.
|
|
623
|
+
* @param messageValidators - The message validators to run.
|
|
624
|
+
* @returns The validation outcome.
|
|
625
|
+
*/
|
|
626
|
+
private async runValidations(
|
|
627
|
+
tx: Tx,
|
|
628
|
+
messageValidators: Record<string, MessageValidator>,
|
|
629
|
+
): Promise<ValidationOutcome> {
|
|
630
|
+
const validationPromises = Object.entries(messageValidators).map(async ([name, { validator, severity }]) => {
|
|
631
|
+
const isValid = await validator.validateTx(tx);
|
|
632
|
+
return { name, isValid, severity };
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
// A promise that resolves when all validations have been run
|
|
636
|
+
const allValidations = Promise.all(validationPromises);
|
|
637
|
+
|
|
638
|
+
// A promise that resolves when the first validation fails
|
|
639
|
+
const firstFailure = Promise.race(
|
|
640
|
+
validationPromises.map(async promise => {
|
|
641
|
+
const result = await promise;
|
|
642
|
+
return result.isValid ? new Promise(() => {}) : result;
|
|
643
|
+
}),
|
|
644
|
+
);
|
|
645
|
+
|
|
646
|
+
// Wait for the first validation to fail or all validations to pass
|
|
647
|
+
const result = await Promise.race([
|
|
648
|
+
allValidations.then(() => ({ allPassed: true as const })),
|
|
649
|
+
firstFailure.then(failure => ({ allPassed: false as const, failure: failure as ValidationResult })),
|
|
650
|
+
]);
|
|
651
|
+
|
|
652
|
+
// If all validations pass, allPassed will be true, if failed, then the failure will be the first validation to fail
|
|
653
|
+
return result;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* Handle a double spend failure.
|
|
658
|
+
*
|
|
659
|
+
* Double spend failures are managed on their own because they are a special case.
|
|
660
|
+
* We must check if the double spend is recent or old, if it is past a threshold, then we heavily penalize the peer.
|
|
661
|
+
*
|
|
662
|
+
* @param tx - The tx that failed the double spend validator.
|
|
663
|
+
* @param blockNumber - The block number of the tx.
|
|
664
|
+
* @param peerId - The peer ID of the peer that sent the tx.
|
|
665
|
+
* @returns True if the tx is valid, false otherwise.
|
|
666
|
+
*/
|
|
667
|
+
private async handleDoubleSpendFailure(tx: Tx, blockNumber: number, peerId: PeerId): Promise<boolean> {
|
|
668
|
+
if (blockNumber <= this.config.severePeerPenaltyBlockLength) {
|
|
569
669
|
return false;
|
|
570
670
|
}
|
|
571
671
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
672
|
+
const snapshotValidator = new DoubleSpendTxValidator({
|
|
673
|
+
getNullifierIndices: (nullifiers: Buffer[]) => {
|
|
674
|
+
const merkleTree = this.worldStateSynchronizer.getSnapshot(
|
|
675
|
+
blockNumber - this.config.severePeerPenaltyBlockLength,
|
|
676
|
+
);
|
|
677
|
+
return merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers);
|
|
678
|
+
},
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
const validSnapshot = await snapshotValidator.validateTx(tx);
|
|
682
|
+
if (!validSnapshot) {
|
|
683
|
+
this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError);
|
|
578
684
|
return false;
|
|
579
685
|
}
|
|
580
686
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type PeerInfo } from '@aztec/circuit-types';
|
|
2
2
|
import { createLogger } from '@aztec/foundation/log';
|
|
3
|
+
import { type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
|
|
3
4
|
|
|
4
5
|
import { type ENR } from '@chainsafe/enr';
|
|
5
6
|
import { type PeerId } from '@libp2p/interface';
|
|
@@ -8,7 +9,7 @@ import { inspect } from 'util';
|
|
|
8
9
|
|
|
9
10
|
import { type P2PConfig } from '../config.js';
|
|
10
11
|
import { type PubSubLibp2p } from '../util.js';
|
|
11
|
-
import { type PeerErrorSeverity, PeerScoring } from './peer_scoring.js';
|
|
12
|
+
import { type PeerErrorSeverity, PeerScoring } from './peer-scoring/peer_scoring.js';
|
|
12
13
|
import { type PeerDiscoveryService } from './service.js';
|
|
13
14
|
|
|
14
15
|
const MAX_DIAL_ATTEMPTS = 3;
|
|
@@ -21,7 +22,7 @@ type CachedPeer = {
|
|
|
21
22
|
dialAttempts: number;
|
|
22
23
|
};
|
|
23
24
|
|
|
24
|
-
export class PeerManager {
|
|
25
|
+
export class PeerManager implements Traceable {
|
|
25
26
|
private cachedPeers: Map<string, CachedPeer> = new Map();
|
|
26
27
|
private peerScoring: PeerScoring;
|
|
27
28
|
private heartbeatCounter: number = 0;
|
|
@@ -30,6 +31,7 @@ export class PeerManager {
|
|
|
30
31
|
private libP2PNode: PubSubLibp2p,
|
|
31
32
|
private peerDiscoveryService: PeerDiscoveryService,
|
|
32
33
|
private config: P2PConfig,
|
|
34
|
+
public readonly tracer: Tracer,
|
|
33
35
|
private logger = createLogger('p2p:peer-manager'),
|
|
34
36
|
) {
|
|
35
37
|
this.peerScoring = new PeerScoring(config);
|
|
@@ -59,6 +61,7 @@ export class PeerManager {
|
|
|
59
61
|
});
|
|
60
62
|
}
|
|
61
63
|
|
|
64
|
+
@trackSpan('PeerManager.heartbeat')
|
|
62
65
|
public heartbeat() {
|
|
63
66
|
this.heartbeatCounter++;
|
|
64
67
|
this.discover();
|
|
@@ -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
|
|