@aztec/p2p 0.0.1-commit.2d9cb6034 → 0.0.1-commit.2e20a94
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/client/p2p_client.d.ts +1 -1
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +9 -2
- package/dest/config.d.ts +103 -99
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +10 -5
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +4 -2
- package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool.js +5 -3
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +2 -2
- package/dest/mem_pools/tx_pool_v2/tx_metadata.js +1 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +5 -2
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.js +17 -9
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +4 -2
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +2 -2
- package/dest/msg_validators/clock_tolerance.d.ts +12 -1
- package/dest/msg_validators/clock_tolerance.d.ts.map +1 -1
- package/dest/msg_validators/clock_tolerance.js +50 -0
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +2 -1
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +2 -1
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +3 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.js +16 -8
- package/dest/msg_validators/tx_validator/archive_cache.js +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.d.ts +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.js +11 -9
- package/dest/services/data_store.d.ts +1 -1
- package/dest/services/data_store.d.ts.map +1 -1
- package/dest/services/data_store.js +5 -5
- package/dest/services/dummy_service.d.ts +2 -1
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +1 -0
- package/dest/services/gossipsub/topic_score_params.d.ts +13 -2
- package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -1
- package/dest/services/gossipsub/topic_score_params.js +21 -4
- package/dest/services/libp2p/libp2p_service.d.ts +2 -15
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +17 -55
- package/dest/services/peer-manager/peer_manager.d.ts +1 -1
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +15 -2
- package/dest/services/peer-manager/peer_scoring.d.ts +3 -1
- package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_scoring.js +4 -0
- package/dest/services/reqresp/config.d.ts +3 -3
- package/dest/services/reqresp/config.d.ts.map +1 -1
- package/dest/services/reqresp/interface.d.ts +14 -9
- package/dest/services/reqresp/interface.d.ts.map +1 -1
- package/dest/services/reqresp/interface.js +10 -11
- package/dest/services/reqresp/metrics.d.ts +1 -1
- package/dest/services/reqresp/metrics.d.ts.map +1 -1
- package/dest/services/reqresp/metrics.js +0 -1
- package/dest/services/reqresp/protocols/index.d.ts +1 -2
- package/dest/services/reqresp/protocols/index.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/index.js +0 -1
- package/dest/services/reqresp/protocols/tx.d.ts +1 -1
- package/dest/services/reqresp/protocols/tx.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/tx.js +1 -3
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limits.d.ts.map +1 -1
- package/dest/services/reqresp/rate-limiter/rate_limits.js +0 -10
- package/dest/services/reqresp/reqresp.d.ts +4 -2
- package/dest/services/reqresp/reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +11 -2
- package/dest/test-helpers/mock-pubsub.d.ts +11 -3
- package/dest/test-helpers/mock-pubsub.d.ts.map +1 -1
- package/dest/test-helpers/mock-pubsub.js +35 -10
- package/dest/test-helpers/reqresp-nodes.d.ts +1 -1
- package/dest/test-helpers/reqresp-nodes.d.ts.map +1 -1
- package/dest/test-helpers/reqresp-nodes.js +1 -2
- package/dest/test-helpers/testbench-utils.d.ts +1 -1
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.js +1 -0
- package/dest/testbench/p2p_client_testbench_worker.js +22 -0
- package/dest/testbench/worker_client_manager.d.ts +8 -1
- package/dest/testbench/worker_client_manager.d.ts.map +1 -1
- package/dest/testbench/worker_client_manager.js +49 -0
- package/package.json +14 -14
- package/src/client/p2p_client.ts +11 -3
- package/src/config.ts +18 -6
- package/src/mem_pools/attestation_pool/attestation_pool.ts +5 -3
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +3 -3
- package/src/mem_pools/tx_pool_v2/tx_metadata.ts +1 -1
- package/src/msg_validators/attestation_validator/attestation_validator.ts +18 -7
- package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +4 -1
- package/src/msg_validators/clock_tolerance.ts +68 -0
- package/src/msg_validators/proposal_validator/block_proposal_validator.ts +4 -1
- package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +4 -1
- package/src/msg_validators/proposal_validator/proposal_validator.ts +13 -7
- package/src/msg_validators/tx_validator/archive_cache.ts +1 -1
- package/src/msg_validators/tx_validator/gas_validator.ts +25 -9
- package/src/services/data_store.ts +5 -13
- package/src/services/dummy_service.ts +1 -0
- package/src/services/gossipsub/topic_score_params.ts +36 -4
- package/src/services/libp2p/libp2p_service.ts +13 -55
- package/src/services/peer-manager/peer_manager.ts +17 -2
- package/src/services/peer-manager/peer_scoring.ts +6 -0
- package/src/services/reqresp/config.ts +2 -2
- package/src/services/reqresp/interface.ts +21 -11
- package/src/services/reqresp/metrics.ts +0 -1
- package/src/services/reqresp/protocols/index.ts +0 -1
- package/src/services/reqresp/protocols/tx.ts +1 -3
- package/src/services/reqresp/rate-limiter/rate_limits.ts +0 -10
- package/src/services/reqresp/reqresp.ts +18 -1
- package/src/test-helpers/mock-pubsub.ts +31 -5
- package/src/test-helpers/reqresp-nodes.ts +2 -2
- package/src/test-helpers/testbench-utils.ts +1 -0
- package/src/testbench/p2p_client_testbench_worker.ts +27 -0
- package/src/testbench/worker_client_manager.ts +55 -0
- package/dest/services/reqresp/protocols/block.d.ts +0 -9
- package/dest/services/reqresp/protocols/block.d.ts.map +0 -1
- package/dest/services/reqresp/protocols/block.js +0 -32
- package/src/services/reqresp/protocols/block.ts +0 -37
|
@@ -317,6 +317,55 @@ class WorkerClientManager {
|
|
|
317
317
|
this.logger.info('All worker processes cleaned up');
|
|
318
318
|
}
|
|
319
319
|
/**
|
|
320
|
+
* Checks that the aggregator (client 0) has sufficient peer connections before running a benchmark.
|
|
321
|
+
* This prevents benchmark cases from starting with degraded connectivity after a previous case
|
|
322
|
+
* caused connection failures.
|
|
323
|
+
*/ async waitForConnectivity(minPeers, timeoutMs = 15_000) {
|
|
324
|
+
const waitInterval = 1000;
|
|
325
|
+
let waited = 0;
|
|
326
|
+
while(waited < timeoutMs){
|
|
327
|
+
const count = await this.getPeerCount(0, 5000);
|
|
328
|
+
if (count >= minPeers) {
|
|
329
|
+
this.logger.info(`Connectivity check passed: ${count}/${minPeers} peers connected`);
|
|
330
|
+
return count;
|
|
331
|
+
}
|
|
332
|
+
this.logger.debug(`Waiting for connectivity: ${count}/${minPeers} (waited ${waited}ms)`);
|
|
333
|
+
await sleep(waitInterval);
|
|
334
|
+
waited += waitInterval;
|
|
335
|
+
}
|
|
336
|
+
const finalCount = await this.getPeerCount(0, 5000);
|
|
337
|
+
this.logger.warn(`Connectivity check: only ${finalCount}/${minPeers} peers after ${timeoutMs}ms`);
|
|
338
|
+
return finalCount;
|
|
339
|
+
}
|
|
340
|
+
getPeerCount(clientIndex, timeoutMs) {
|
|
341
|
+
return new Promise((resolve)=>{
|
|
342
|
+
let resolved = false;
|
|
343
|
+
const handler = (msg)=>{
|
|
344
|
+
if (resolved) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
if (msg.type === 'PEER_COUNT') {
|
|
348
|
+
resolved = true;
|
|
349
|
+
clearTimeout(timeout);
|
|
350
|
+
this.processes[clientIndex].off('message', handler);
|
|
351
|
+
resolve(msg.count);
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
const timeout = setTimeout(()=>{
|
|
355
|
+
if (resolved) {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
resolved = true;
|
|
359
|
+
this.processes[clientIndex].off('message', handler);
|
|
360
|
+
resolve(0);
|
|
361
|
+
}, timeoutMs);
|
|
362
|
+
this.processes[clientIndex].on('message', handler);
|
|
363
|
+
this.processes[clientIndex].send({
|
|
364
|
+
type: 'GET_PEER_COUNT'
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
320
369
|
* Run a req/resp benchmark across all worker clients.
|
|
321
370
|
*
|
|
322
371
|
* This sends a BENCH_REQRESP command to all workers:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/p2p",
|
|
3
|
-
"version": "0.0.1-commit.
|
|
3
|
+
"version": "0.0.1-commit.2e20a94",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js",
|
|
@@ -67,17 +67,17 @@
|
|
|
67
67
|
]
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@aztec/constants": "0.0.1-commit.
|
|
71
|
-
"@aztec/epoch-cache": "0.0.1-commit.
|
|
72
|
-
"@aztec/ethereum": "0.0.1-commit.
|
|
73
|
-
"@aztec/foundation": "0.0.1-commit.
|
|
74
|
-
"@aztec/kv-store": "0.0.1-commit.
|
|
75
|
-
"@aztec/noir-contracts.js": "0.0.1-commit.
|
|
76
|
-
"@aztec/noir-protocol-circuits-types": "0.0.1-commit.
|
|
77
|
-
"@aztec/protocol-contracts": "0.0.1-commit.
|
|
78
|
-
"@aztec/simulator": "0.0.1-commit.
|
|
79
|
-
"@aztec/stdlib": "0.0.1-commit.
|
|
80
|
-
"@aztec/telemetry-client": "0.0.1-commit.
|
|
70
|
+
"@aztec/constants": "0.0.1-commit.2e20a94",
|
|
71
|
+
"@aztec/epoch-cache": "0.0.1-commit.2e20a94",
|
|
72
|
+
"@aztec/ethereum": "0.0.1-commit.2e20a94",
|
|
73
|
+
"@aztec/foundation": "0.0.1-commit.2e20a94",
|
|
74
|
+
"@aztec/kv-store": "0.0.1-commit.2e20a94",
|
|
75
|
+
"@aztec/noir-contracts.js": "0.0.1-commit.2e20a94",
|
|
76
|
+
"@aztec/noir-protocol-circuits-types": "0.0.1-commit.2e20a94",
|
|
77
|
+
"@aztec/protocol-contracts": "0.0.1-commit.2e20a94",
|
|
78
|
+
"@aztec/simulator": "0.0.1-commit.2e20a94",
|
|
79
|
+
"@aztec/stdlib": "0.0.1-commit.2e20a94",
|
|
80
|
+
"@aztec/telemetry-client": "0.0.1-commit.2e20a94",
|
|
81
81
|
"@chainsafe/libp2p-gossipsub": "13.0.0",
|
|
82
82
|
"@chainsafe/libp2p-noise": "^15.0.0",
|
|
83
83
|
"@chainsafe/libp2p-yamux": "^6.0.2",
|
|
@@ -104,8 +104,8 @@
|
|
|
104
104
|
"xxhash-wasm": "^1.1.0"
|
|
105
105
|
},
|
|
106
106
|
"devDependencies": {
|
|
107
|
-
"@aztec/archiver": "0.0.1-commit.
|
|
108
|
-
"@aztec/world-state": "0.0.1-commit.
|
|
107
|
+
"@aztec/archiver": "0.0.1-commit.2e20a94",
|
|
108
|
+
"@aztec/world-state": "0.0.1-commit.2e20a94",
|
|
109
109
|
"@jest/globals": "^30.0.0",
|
|
110
110
|
"@types/jest": "^30.0.0",
|
|
111
111
|
"@types/node": "^22.15.17",
|
package/src/client/p2p_client.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { GENESIS_BLOCK_HEADER_HASH } from '@aztec/constants';
|
|
2
1
|
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
3
2
|
import { BlockNumber, CheckpointNumber, SlotNumber } from '@aztec/foundation/branded-types';
|
|
4
3
|
import { createLogger } from '@aztec/foundation/log';
|
|
@@ -9,6 +8,7 @@ import { L2TipsKVStore } from '@aztec/kv-store/stores';
|
|
|
9
8
|
import {
|
|
10
9
|
type CheckpointId,
|
|
11
10
|
type EthAddress,
|
|
11
|
+
GENESIS_BLOCK_HEADER_HASH,
|
|
12
12
|
type L2Block,
|
|
13
13
|
type L2BlockId,
|
|
14
14
|
type L2BlockSource,
|
|
@@ -257,7 +257,11 @@ export class P2PClient extends WithTracer implements P2P {
|
|
|
257
257
|
});
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
-
|
|
260
|
+
// Should never happen: all branches above call initBlockStream()
|
|
261
|
+
if (!this.blockStream) {
|
|
262
|
+
throw new Error('Block stream not initialized');
|
|
263
|
+
}
|
|
264
|
+
this.blockStream.start();
|
|
261
265
|
await this.txCollection.start();
|
|
262
266
|
this.txFileStore?.start();
|
|
263
267
|
|
|
@@ -319,7 +323,11 @@ export class P2PClient extends WithTracer implements P2P {
|
|
|
319
323
|
/** Triggers a sync to the archiver. Used for testing. */
|
|
320
324
|
public async sync() {
|
|
321
325
|
this.initBlockStream();
|
|
322
|
-
|
|
326
|
+
// Should never happen: initBlockStream() creates blockStream if absent
|
|
327
|
+
if (!this.blockStream) {
|
|
328
|
+
throw new Error('Block stream not initialized');
|
|
329
|
+
}
|
|
330
|
+
await this.blockStream.sync();
|
|
323
331
|
}
|
|
324
332
|
|
|
325
333
|
@trackSpan('p2pClient.broadcastProposal', async proposal => ({
|
package/src/config.ts
CHANGED
|
@@ -39,7 +39,14 @@ export interface P2PConfig
|
|
|
39
39
|
ChainConfig,
|
|
40
40
|
TxCollectionConfig,
|
|
41
41
|
TxFileStoreConfig,
|
|
42
|
-
Pick<
|
|
42
|
+
Pick<
|
|
43
|
+
SequencerConfig,
|
|
44
|
+
| 'blockDurationMs'
|
|
45
|
+
| 'expectedBlockProposalsPerSlot'
|
|
46
|
+
| 'l1PublishingTime'
|
|
47
|
+
| 'maxTxsPerBlock'
|
|
48
|
+
| 'attestationPropagationTime'
|
|
49
|
+
> {
|
|
43
50
|
/** Maximum transactions per block for validation. Overrides maxTxsPerBlock for gossip validation when set. */
|
|
44
51
|
validateMaxTxsPerBlock?: number;
|
|
45
52
|
|
|
@@ -218,23 +225,23 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
|
|
|
218
225
|
env: 'VALIDATOR_MAX_TX_PER_BLOCK',
|
|
219
226
|
description:
|
|
220
227
|
'Maximum transactions per block for validation. Overrides maxTxsPerBlock for gossip validation when set.',
|
|
221
|
-
parseEnv: (val: string) =>
|
|
228
|
+
parseEnv: (val: string) => parseInt(val, 10),
|
|
222
229
|
},
|
|
223
230
|
validateMaxTxsPerCheckpoint: {
|
|
224
231
|
env: 'VALIDATOR_MAX_TX_PER_CHECKPOINT',
|
|
225
232
|
description:
|
|
226
233
|
'Maximum transactions per checkpoint for validation. Used as fallback for maxTxsPerBlock when that is not set.',
|
|
227
|
-
parseEnv: (val: string) =>
|
|
234
|
+
parseEnv: (val: string) => parseInt(val, 10),
|
|
228
235
|
},
|
|
229
236
|
validateMaxL2BlockGas: {
|
|
230
237
|
env: 'VALIDATOR_MAX_L2_BLOCK_GAS',
|
|
231
238
|
description: 'Maximum L2 gas per block for validation. When set, txs exceeding this limit are rejected.',
|
|
232
|
-
parseEnv: (val: string) =>
|
|
239
|
+
parseEnv: (val: string) => parseInt(val, 10),
|
|
233
240
|
},
|
|
234
241
|
validateMaxDABlockGas: {
|
|
235
242
|
env: 'VALIDATOR_MAX_DA_BLOCK_GAS',
|
|
236
243
|
description: 'Maximum DA gas per block for validation. When set, txs exceeding this limit are rejected.',
|
|
237
|
-
parseEnv: (val: string) =>
|
|
244
|
+
parseEnv: (val: string) => parseInt(val, 10),
|
|
238
245
|
},
|
|
239
246
|
p2pEnabled: {
|
|
240
247
|
env: 'P2P_ENABLED',
|
|
@@ -436,7 +443,7 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
|
|
|
436
443
|
},
|
|
437
444
|
p2pStoreMapSizeKb: {
|
|
438
445
|
env: 'P2P_STORE_MAP_SIZE_KB',
|
|
439
|
-
parseEnv: (val: string
|
|
446
|
+
parseEnv: (val: string) => +val,
|
|
440
447
|
description: 'The maximum possible size of the P2P DB in KB. Overwrites the general dataStoreMapSizeKb.',
|
|
441
448
|
},
|
|
442
449
|
txPublicSetupAllowListExtend: {
|
|
@@ -495,6 +502,11 @@ export const p2pConfigMappings: ConfigMappingsType<P2PConfig> = {
|
|
|
495
502
|
description: 'Alters the format of p2p messages to include things like broadcast timestamp FOR TESTING ONLY',
|
|
496
503
|
...booleanConfigHelper(false),
|
|
497
504
|
},
|
|
505
|
+
l1PublishingTime: {
|
|
506
|
+
env: 'SEQ_L1_PUBLISHING_TIME_ALLOWANCE_IN_SLOT',
|
|
507
|
+
description: 'How much time (in seconds) we allow in the slot for publishing the L1 tx (defaults to 1 L1 slot).',
|
|
508
|
+
parseEnv: (val: string) => parseInt(val, 10),
|
|
509
|
+
},
|
|
498
510
|
fishermanMode: {
|
|
499
511
|
env: 'FISHERMAN_MODE',
|
|
500
512
|
description:
|
|
@@ -154,14 +154,16 @@ export class AttestationPool {
|
|
|
154
154
|
/** Maximum indexWithinCheckpoint value (2^10 - 1 = 1023). */
|
|
155
155
|
private static readonly MAX_INDEX = (1 << AttestationPool.INDEX_BITS) - 1;
|
|
156
156
|
|
|
157
|
-
/** Creates a position key for block proposals:
|
|
157
|
+
/** Creates a position key for block proposals: slot * 1024 + indexWithinCheckpoint.
|
|
158
|
+
* Uses multiplication instead of bit-shift to avoid 32-bit signed integer overflow
|
|
159
|
+
* (bit-shift overflows after slot ~2^21, roughly 278 days of uptime). */
|
|
158
160
|
private getBlockPositionKey(slot: number, indexWithinCheckpoint: number): number {
|
|
159
161
|
if (indexWithinCheckpoint > AttestationPool.MAX_INDEX) {
|
|
160
162
|
throw new Error(
|
|
161
163
|
`Value for indexWithinCheckpoint ${indexWithinCheckpoint} exceeds maximum ${AttestationPool.MAX_INDEX}`,
|
|
162
164
|
);
|
|
163
165
|
}
|
|
164
|
-
return
|
|
166
|
+
return slot * (1 << AttestationPool.INDEX_BITS) + indexWithinCheckpoint;
|
|
165
167
|
}
|
|
166
168
|
|
|
167
169
|
/**
|
|
@@ -454,7 +456,7 @@ export class AttestationPool {
|
|
|
454
456
|
|
|
455
457
|
// Delete block proposals for slots < oldestSlot, using blockProposalsForSlotAndIndex as index
|
|
456
458
|
// Key format: (slot << INDEX_BITS) | indexWithinCheckpoint
|
|
457
|
-
const blockPositionEndKey = oldestSlot << AttestationPool.INDEX_BITS;
|
|
459
|
+
const blockPositionEndKey = oldestSlot * (1 << AttestationPool.INDEX_BITS);
|
|
458
460
|
for await (const positionKey of this.blockProposalsForSlotAndIndex.keysAsync({ end: blockPositionEndKey })) {
|
|
459
461
|
const proposalIds = await toArray(this.blockProposalsForSlotAndIndex.getValuesAsync(positionKey));
|
|
460
462
|
for (const proposalId of proposalIds) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
2
1
|
import { createLogger } from '@aztec/foundation/log';
|
|
2
|
+
import { BlockHash } from '@aztec/stdlib/block';
|
|
3
3
|
import type { WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
4
4
|
import { MerkleTreeId } from '@aztec/stdlib/trees';
|
|
5
5
|
|
|
@@ -33,14 +33,14 @@ export class InvalidTxsAfterReorgRule implements EvictionRule {
|
|
|
33
33
|
const pendingTxs = pool.getPendingTxs();
|
|
34
34
|
|
|
35
35
|
// Deduplicate block hashes to reduce redundant DB lookups
|
|
36
|
-
const uniqueBlockHashes = new Map<string,
|
|
36
|
+
const uniqueBlockHashes = new Map<string, BlockHash>();
|
|
37
37
|
const txsByBlockHash = new Map<string, string[]>();
|
|
38
38
|
|
|
39
39
|
for (const meta of pendingTxs) {
|
|
40
40
|
const blockHashStr = meta.anchorBlockHeaderHash;
|
|
41
41
|
if (!txsByBlockHash.has(blockHashStr)) {
|
|
42
42
|
txsByBlockHash.set(blockHashStr, []);
|
|
43
|
-
uniqueBlockHashes.set(blockHashStr,
|
|
43
|
+
uniqueBlockHashes.set(blockHashStr, BlockHash.fromString(blockHashStr));
|
|
44
44
|
}
|
|
45
45
|
txsByBlockHash.get(blockHashStr)!.push(meta.txHash);
|
|
46
46
|
}
|
|
@@ -291,7 +291,7 @@ export function stubTxMetaValidationData(overrides: { expirationTimestamp?: bigi
|
|
|
291
291
|
expirationTimestamp: overrides.expirationTimestamp ?? 0n,
|
|
292
292
|
constants: {
|
|
293
293
|
anchorBlockHeader: {
|
|
294
|
-
hash: () => Promise.resolve(
|
|
294
|
+
hash: () => Promise.resolve(BlockHash.ZERO),
|
|
295
295
|
globalVariables: { blockNumber: BlockNumber(0) },
|
|
296
296
|
},
|
|
297
297
|
txContext: {
|
|
@@ -8,14 +8,21 @@ import {
|
|
|
8
8
|
type ValidationResult,
|
|
9
9
|
} from '@aztec/stdlib/p2p';
|
|
10
10
|
|
|
11
|
-
import { isWithinClockTolerance } from '../clock_tolerance.js';
|
|
11
|
+
import { PipeliningWindow, isWithinClockTolerance } from '../clock_tolerance.js';
|
|
12
12
|
|
|
13
13
|
export class CheckpointAttestationValidator implements P2PValidator<CheckpointAttestation> {
|
|
14
14
|
protected epochCache: EpochCacheInterface;
|
|
15
15
|
protected logger: Logger;
|
|
16
|
+
private readonly pipeliningWindow: PipeliningWindow;
|
|
16
17
|
|
|
17
|
-
constructor(
|
|
18
|
+
constructor(
|
|
19
|
+
epochCache: EpochCacheInterface,
|
|
20
|
+
opts: {
|
|
21
|
+
l1PublishingTime?: number;
|
|
22
|
+
},
|
|
23
|
+
) {
|
|
18
24
|
this.epochCache = epochCache;
|
|
25
|
+
this.pipeliningWindow = new PipeliningWindow(epochCache, { l1PublishingTime: opts.l1PublishingTime });
|
|
19
26
|
this.logger = createLogger('p2p:checkpoint-attestation-validator');
|
|
20
27
|
}
|
|
21
28
|
|
|
@@ -23,19 +30,23 @@ export class CheckpointAttestationValidator implements P2PValidator<CheckpointAt
|
|
|
23
30
|
const slotNumber = message.payload.header.slotNumber;
|
|
24
31
|
|
|
25
32
|
try {
|
|
26
|
-
// Use target slots since proposals target pipeline slots (slot + 1 when pipelining)
|
|
33
|
+
// Use target slots since proposals target pipeline slots (slot + 1 when pipelining).
|
|
27
34
|
const { targetSlot, nextSlot } = this.epochCache.getTargetAndNextSlot();
|
|
28
35
|
|
|
29
36
|
if (slotNumber !== targetSlot && slotNumber !== nextSlot) {
|
|
30
|
-
//
|
|
31
|
-
|
|
37
|
+
// When pipelining, accept attestations for the current slot (built in the previous slot)
|
|
38
|
+
// until the target slot reaches its L1 publish cutoff.
|
|
39
|
+
if (this.pipeliningWindow.acceptsAttestation(slotNumber)) {
|
|
40
|
+
// Fall through to remaining validation (signature, committee, etc.)
|
|
41
|
+
} else if (!isWithinClockTolerance(slotNumber, targetSlot, this.epochCache)) {
|
|
32
42
|
this.logger.warn(
|
|
33
43
|
`Checkpoint attestation slot ${slotNumber} is not current (${targetSlot}) or next (${nextSlot}) slot`,
|
|
34
44
|
);
|
|
35
45
|
return { result: 'reject', severity: PeerErrorSeverity.HighToleranceError };
|
|
46
|
+
} else {
|
|
47
|
+
this.logger.debug(`Ignoring checkpoint attestation for previous slot ${slotNumber} within clock tolerance`);
|
|
48
|
+
return { result: 'ignore' };
|
|
36
49
|
}
|
|
37
|
-
this.logger.debug(`Ignoring checkpoint attestation for previous slot ${slotNumber} within clock tolerance`);
|
|
38
|
-
return { result: 'ignore' };
|
|
39
50
|
}
|
|
40
51
|
|
|
41
52
|
// Verify the signature is valid
|
|
@@ -20,8 +20,11 @@ export class FishermanAttestationValidator extends CheckpointAttestationValidato
|
|
|
20
20
|
epochCache: EpochCacheInterface,
|
|
21
21
|
private attestationPool: AttestationPoolApi,
|
|
22
22
|
telemetryClient: TelemetryClient,
|
|
23
|
+
opts: {
|
|
24
|
+
l1PublishingTime?: number;
|
|
25
|
+
} = {},
|
|
23
26
|
) {
|
|
24
|
-
super(epochCache);
|
|
27
|
+
super(epochCache, opts);
|
|
25
28
|
this.logger = this.logger.createChild('[FISHERMAN]');
|
|
26
29
|
|
|
27
30
|
const meter = telemetryClient.getMeter('FishermanAttestationValidator');
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
2
2
|
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
3
|
+
import { DEFAULT_P2P_PROPAGATION_TIME, createPipelinedCheckpointTimingModel } from '@aztec/stdlib/timetable';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Maximum clock disparity tolerance for P2P message validation (in milliseconds).
|
|
@@ -50,3 +51,70 @@ export function isWithinClockTolerance(
|
|
|
50
51
|
|
|
51
52
|
return elapsedMs < MAXIMUM_GOSSIP_CLOCK_DISPARITY_MS;
|
|
52
53
|
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Checks if a message should be accepted under the pipelining grace period.
|
|
57
|
+
*
|
|
58
|
+
* When pipelining is enabled, `targetSlot = slotNow + 1`. A proposal built in slot N-1
|
|
59
|
+
* for slot N arrives when validators are in slot N, so their `targetSlot = N+1`.
|
|
60
|
+
* This function accepts proposals for the current wallclock slot if we're within the
|
|
61
|
+
* first `windowSeconds` seconds of the slot (the pipelining grace period). - see stdlib/timetable/index.ts
|
|
62
|
+
*
|
|
63
|
+
* @param messageSlot - The slot number from the received message
|
|
64
|
+
* @param epochCache - EpochCache to get timing and pipelining state
|
|
65
|
+
* @param windowSeconds - The window grace period allowed for attestations into the next slot
|
|
66
|
+
* @returns true if pipelining is enabled, the message is for the current slot, and we're within the grace period
|
|
67
|
+
*/
|
|
68
|
+
function isWithinPipeliningWindow(
|
|
69
|
+
messageSlot: SlotNumber,
|
|
70
|
+
epochCache: EpochCacheInterface,
|
|
71
|
+
windowSeconds: number,
|
|
72
|
+
): boolean {
|
|
73
|
+
if (!epochCache.isProposerPipeliningEnabled()) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const currentSlot = epochCache.getSlotNow();
|
|
78
|
+
if (messageSlot !== currentSlot) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const { ts: slotStartTs, nowMs } = epochCache.getEpochAndSlotNow();
|
|
83
|
+
const slotStartMs = slotStartTs * 1000n;
|
|
84
|
+
const elapsedMs = Number(nowMs - slotStartMs);
|
|
85
|
+
const windowMs = windowSeconds * 1000 + MAXIMUM_GOSSIP_CLOCK_DISPARITY_MS;
|
|
86
|
+
|
|
87
|
+
return elapsedMs < windowMs;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export class PipeliningWindow {
|
|
91
|
+
private readonly proposalWindowIntoTargetSlot: number;
|
|
92
|
+
private readonly attestationWindowIntoTargetSlot: number;
|
|
93
|
+
|
|
94
|
+
constructor(
|
|
95
|
+
private readonly epochCache: EpochCacheInterface,
|
|
96
|
+
opts: {
|
|
97
|
+
p2pPropagationTime?: number;
|
|
98
|
+
l1PublishingTime?: number;
|
|
99
|
+
} = {},
|
|
100
|
+
) {
|
|
101
|
+
const l1Constants = epochCache.getL1Constants();
|
|
102
|
+
const checkpointTiming = createPipelinedCheckpointTimingModel({
|
|
103
|
+
aztecSlotDuration: l1Constants.slotDuration,
|
|
104
|
+
ethereumSlotDuration: l1Constants.ethereumSlotDuration,
|
|
105
|
+
l1PublishingTime: opts.l1PublishingTime ?? l1Constants.ethereumSlotDuration,
|
|
106
|
+
p2pPropagationTime: opts.p2pPropagationTime ?? DEFAULT_P2P_PROPAGATION_TIME,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
this.proposalWindowIntoTargetSlot = checkpointTiming.proposalWindowIntoTargetSlot;
|
|
110
|
+
this.attestationWindowIntoTargetSlot = checkpointTiming.attestationWindowIntoTargetSlot;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public acceptsProposal(messageSlot: SlotNumber): boolean {
|
|
114
|
+
return isWithinPipeliningWindow(messageSlot, this.epochCache, this.proposalWindowIntoTargetSlot);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public acceptsAttestation(messageSlot: SlotNumber): boolean {
|
|
118
|
+
return isWithinPipeliningWindow(messageSlot, this.epochCache, this.attestationWindowIntoTargetSlot);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -6,7 +6,10 @@ import { ProposalValidator } from '../proposal_validator/proposal_validator.js';
|
|
|
6
6
|
export class BlockProposalValidator implements P2PValidator<BlockProposal> {
|
|
7
7
|
private proposalValidator: ProposalValidator;
|
|
8
8
|
|
|
9
|
-
constructor(
|
|
9
|
+
constructor(
|
|
10
|
+
epochCache: EpochCacheInterface,
|
|
11
|
+
opts: { txsPermitted: boolean; maxTxsPerBlock?: number; p2pPropagationTime?: number },
|
|
12
|
+
) {
|
|
10
13
|
this.proposalValidator = new ProposalValidator(epochCache, opts, 'p2p:block_proposal_validator');
|
|
11
14
|
}
|
|
12
15
|
|
|
@@ -6,7 +6,10 @@ import { ProposalValidator } from '../proposal_validator/proposal_validator.js';
|
|
|
6
6
|
export class CheckpointProposalValidator implements P2PValidator<CheckpointProposal> {
|
|
7
7
|
private proposalValidator: ProposalValidator;
|
|
8
8
|
|
|
9
|
-
constructor(
|
|
9
|
+
constructor(
|
|
10
|
+
epochCache: EpochCacheInterface,
|
|
11
|
+
opts: { txsPermitted: boolean; maxTxsPerBlock?: number; p2pPropagationTime?: number },
|
|
12
|
+
) {
|
|
10
13
|
this.proposalValidator = new ProposalValidator(epochCache, opts, 'p2p:checkpoint_proposal_validator');
|
|
11
14
|
}
|
|
12
15
|
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
type ValidationResult,
|
|
9
9
|
} from '@aztec/stdlib/p2p';
|
|
10
10
|
|
|
11
|
-
import { isWithinClockTolerance } from '../clock_tolerance.js';
|
|
11
|
+
import { PipeliningWindow, isWithinClockTolerance } from '../clock_tolerance.js';
|
|
12
12
|
|
|
13
13
|
/** Validates header-level and tx-level fields of block and checkpoint proposals. */
|
|
14
14
|
export class ProposalValidator {
|
|
@@ -16,33 +16,39 @@ export class ProposalValidator {
|
|
|
16
16
|
private logger: Logger;
|
|
17
17
|
private txsPermitted: boolean;
|
|
18
18
|
private maxTxsPerBlock?: number;
|
|
19
|
+
private pipeliningWindow: PipeliningWindow;
|
|
19
20
|
|
|
20
21
|
constructor(
|
|
21
22
|
epochCache: EpochCacheInterface,
|
|
22
|
-
opts: { txsPermitted: boolean; maxTxsPerBlock?: number },
|
|
23
|
+
opts: { txsPermitted: boolean; maxTxsPerBlock?: number; p2pPropagationTime?: number },
|
|
23
24
|
loggerName: string,
|
|
24
25
|
) {
|
|
25
26
|
this.epochCache = epochCache;
|
|
26
27
|
this.txsPermitted = opts.txsPermitted;
|
|
27
28
|
this.maxTxsPerBlock = opts.maxTxsPerBlock;
|
|
29
|
+
this.pipeliningWindow = new PipeliningWindow(epochCache, { p2pPropagationTime: opts.p2pPropagationTime });
|
|
28
30
|
this.logger = createLogger(loggerName);
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
/** Validates header-level fields: slot, signature, and proposer. */
|
|
32
34
|
public async validate(proposal: BlockProposal | CheckpointProposalCore): Promise<ValidationResult> {
|
|
33
35
|
try {
|
|
34
|
-
// Slot check: use target slots since proposals target pipeline slots (slot + 1 when pipelining)
|
|
36
|
+
// Slot check: use target slots since proposals target pipeline slots (slot + 1 when pipelining).
|
|
35
37
|
const { targetSlot, nextSlot } = this.epochCache.getTargetAndNextSlot();
|
|
36
38
|
|
|
37
39
|
const slotNumber = proposal.slotNumber;
|
|
38
40
|
if (slotNumber !== targetSlot && slotNumber !== nextSlot) {
|
|
39
|
-
//
|
|
40
|
-
if
|
|
41
|
+
// When pipelining, accept proposals for the current slot (built in the previous slot)
|
|
42
|
+
// if they're still within the shared proposal acceptance window.
|
|
43
|
+
if (this.pipeliningWindow.acceptsProposal(slotNumber)) {
|
|
44
|
+
// Fall through to remaining validation (signature, proposer, etc.)
|
|
45
|
+
} else if (!isWithinClockTolerance(slotNumber, targetSlot, this.epochCache)) {
|
|
41
46
|
this.logger.warn(`Penalizing peer for invalid slot number ${slotNumber}`, { targetSlot, nextSlot });
|
|
42
47
|
return { result: 'reject', severity: PeerErrorSeverity.HighToleranceError };
|
|
48
|
+
} else {
|
|
49
|
+
this.logger.verbose(`Ignoring proposal for previous slot ${slotNumber} within clock tolerance`);
|
|
50
|
+
return { result: 'ignore' };
|
|
43
51
|
}
|
|
44
|
-
this.logger.verbose(`Ignoring proposal for previous slot ${slotNumber} within clock tolerance`);
|
|
45
|
-
return { result: 'ignore' };
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
// Signature validity
|
|
@@ -15,7 +15,7 @@ export class ArchiveCache implements ArchiveSource {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
public async getArchiveIndices(archives: BlockHash[]): Promise<(bigint | undefined)[]> {
|
|
18
|
-
const toCheckDb = archives.filter(n => !this.archives.has(n.toString()))
|
|
18
|
+
const toCheckDb = archives.filter(n => !this.archives.has(n.toString()));
|
|
19
19
|
const dbHits = await this.db.findLeafIndices(MerkleTreeId.ARCHIVE, toCheckDb);
|
|
20
20
|
dbHits.forEach((x, index) => {
|
|
21
21
|
if (x !== undefined) {
|
|
@@ -87,7 +87,12 @@ export class GasLimitsValidator<T extends HasGasLimitData> implements TxValidato
|
|
|
87
87
|
gasLimits,
|
|
88
88
|
minGasLimits,
|
|
89
89
|
});
|
|
90
|
-
return {
|
|
90
|
+
return {
|
|
91
|
+
result: 'invalid',
|
|
92
|
+
reason: [
|
|
93
|
+
`${TX_ERROR_INSUFFICIENT_GAS_LIMIT} (required=da:${minGasLimits.daGas},l2:${minGasLimits.l2Gas} got=da:${gasLimits.daGas},l2:${gasLimits.l2Gas})`,
|
|
94
|
+
],
|
|
95
|
+
};
|
|
91
96
|
}
|
|
92
97
|
|
|
93
98
|
if (gasLimits.l2Gas > this.#effectiveMaxL2Gas) {
|
|
@@ -97,7 +102,10 @@ export class GasLimitsValidator<T extends HasGasLimitData> implements TxValidato
|
|
|
97
102
|
rollupManaLimit: this.#rollupManaLimit,
|
|
98
103
|
maxBlockL2Gas: this.#maxBlockL2Gas,
|
|
99
104
|
});
|
|
100
|
-
return {
|
|
105
|
+
return {
|
|
106
|
+
result: 'invalid',
|
|
107
|
+
reason: [`${TX_ERROR_GAS_LIMIT_TOO_HIGH} (l2Gas=${gasLimits.l2Gas}, max=${this.#effectiveMaxL2Gas})`],
|
|
108
|
+
};
|
|
101
109
|
}
|
|
102
110
|
|
|
103
111
|
if (gasLimits.daGas > this.#effectiveMaxDAGas) {
|
|
@@ -106,7 +114,10 @@ export class GasLimitsValidator<T extends HasGasLimitData> implements TxValidato
|
|
|
106
114
|
effectiveMaxDAGas: this.#effectiveMaxDAGas,
|
|
107
115
|
maxBlockDAGas: this.#maxBlockDAGas,
|
|
108
116
|
});
|
|
109
|
-
return {
|
|
117
|
+
return {
|
|
118
|
+
result: 'invalid',
|
|
119
|
+
reason: [`${TX_ERROR_GAS_LIMIT_TOO_HIGH} (daGas=${gasLimits.daGas}, max=${this.#effectiveMaxDAGas})`],
|
|
120
|
+
};
|
|
110
121
|
}
|
|
111
122
|
|
|
112
123
|
return { result: 'valid' };
|
|
@@ -157,19 +168,20 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
157
168
|
if (gasLimitValidation.result === 'invalid') {
|
|
158
169
|
return Promise.resolve(gasLimitValidation);
|
|
159
170
|
}
|
|
160
|
-
|
|
161
|
-
|
|
171
|
+
const skipReason = this.#getSkipReason(tx);
|
|
172
|
+
if (skipReason) {
|
|
173
|
+
return Promise.resolve({ result: 'skipped', reason: [skipReason] });
|
|
162
174
|
}
|
|
163
175
|
return await this.validateTxFee(tx);
|
|
164
176
|
}
|
|
165
177
|
|
|
166
178
|
/**
|
|
167
|
-
* Check whether the tx's max fees are valid for the current block, and skip if not.
|
|
179
|
+
* Check whether the tx's max fees are valid for the current block, and return a skip reason if not.
|
|
168
180
|
* We skip instead of invalidating since the tx may become eligible later.
|
|
169
181
|
* Note that circuits check max fees even if fee payer is unset, so we
|
|
170
182
|
* keep this validation even if the tx does not pay fees.
|
|
171
183
|
*/
|
|
172
|
-
#
|
|
184
|
+
#getSkipReason(tx: Tx): string | undefined {
|
|
173
185
|
const gasSettings = tx.data.constants.txContext.gasSettings;
|
|
174
186
|
|
|
175
187
|
// Skip the tx if its max fees are not enough for the current block's gas fees.
|
|
@@ -182,8 +194,9 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
182
194
|
txMaxFeesPerGas: maxFeesPerGas.toInspect(),
|
|
183
195
|
currentGasFees: this.#gasFees.toInspect(),
|
|
184
196
|
});
|
|
197
|
+
return `${TX_ERROR_INSUFFICIENT_FEE_PER_GAS} (maxFee=da:${maxFeesPerGas.feePerDaGas},l2:${maxFeesPerGas.feePerL2Gas} required=da:${this.#gasFees.feePerDaGas},l2:${this.#gasFees.feePerL2Gas})`;
|
|
185
198
|
}
|
|
186
|
-
return
|
|
199
|
+
return undefined;
|
|
187
200
|
}
|
|
188
201
|
|
|
189
202
|
/**
|
|
@@ -212,7 +225,10 @@ export class GasTxValidator implements TxValidator<Tx> {
|
|
|
212
225
|
balance,
|
|
213
226
|
feeLimit,
|
|
214
227
|
});
|
|
215
|
-
return {
|
|
228
|
+
return {
|
|
229
|
+
result: 'invalid',
|
|
230
|
+
reason: [`${TX_ERROR_INSUFFICIENT_FEE_PAYER_BALANCE} (required=${feeLimit}, available=${balance})`],
|
|
231
|
+
};
|
|
216
232
|
}
|
|
217
233
|
return { result: 'valid' };
|
|
218
234
|
}
|
|
@@ -28,8 +28,6 @@ export class AztecDatastore implements Datastore {
|
|
|
28
28
|
#memoryDatastore: Map<string, MemoryItem>;
|
|
29
29
|
#dbDatastore: AztecAsyncMap<string, Uint8Array>;
|
|
30
30
|
|
|
31
|
-
#batchOps: BatchOp[] = [];
|
|
32
|
-
|
|
33
31
|
private maxMemoryItems: number;
|
|
34
32
|
|
|
35
33
|
constructor(db: AztecAsyncKVStore, { maxMemoryItems } = { maxMemoryItems: 50 }) {
|
|
@@ -92,23 +90,17 @@ export class AztecDatastore implements Datastore {
|
|
|
92
90
|
}
|
|
93
91
|
|
|
94
92
|
batch(): Batch {
|
|
93
|
+
const ops: BatchOp[] = [];
|
|
95
94
|
return {
|
|
96
95
|
put: (key, value) => {
|
|
97
|
-
|
|
98
|
-
type: 'put',
|
|
99
|
-
key,
|
|
100
|
-
value,
|
|
101
|
-
});
|
|
96
|
+
ops.push({ type: 'put', key, value });
|
|
102
97
|
},
|
|
103
98
|
delete: key => {
|
|
104
|
-
|
|
105
|
-
type: 'del',
|
|
106
|
-
key,
|
|
107
|
-
});
|
|
99
|
+
ops.push({ type: 'del', key });
|
|
108
100
|
},
|
|
109
101
|
commit: async () => {
|
|
110
102
|
await this.#db.transactionAsync(async () => {
|
|
111
|
-
for (const op of
|
|
103
|
+
for (const op of ops) {
|
|
112
104
|
if (op.type === 'put' && op.value) {
|
|
113
105
|
await this.put(op.key, op.value);
|
|
114
106
|
} else if (op.type === 'del') {
|
|
@@ -116,7 +108,7 @@ export class AztecDatastore implements Datastore {
|
|
|
116
108
|
}
|
|
117
109
|
}
|
|
118
110
|
});
|
|
119
|
-
|
|
111
|
+
ops.length = 0;
|
|
120
112
|
},
|
|
121
113
|
};
|
|
122
114
|
}
|
|
@@ -287,6 +287,7 @@ export class DummyPeerManager implements PeerManagerInterface {
|
|
|
287
287
|
|
|
288
288
|
export class DummyReqResp implements ReqRespInterface {
|
|
289
289
|
updateConfig(_config: Partial<P2PReqRespConfig>): void {}
|
|
290
|
+
setShouldRejectPeer(): void {}
|
|
290
291
|
start(
|
|
291
292
|
_subProtocolHandlers: ReqRespSubProtocolHandlers,
|
|
292
293
|
_subProtocolValidators: ReqRespSubProtocolValidators,
|