@aztec/p2p 0.0.1-commit.993d240 → 0.0.1-commit.9a89641
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/README.md +7 -6
- package/dest/bootstrap/bootstrap.d.ts +1 -1
- package/dest/bootstrap/bootstrap.d.ts.map +1 -1
- package/dest/bootstrap/bootstrap.js +20 -12
- package/dest/client/factory.d.ts +2 -2
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +10 -5
- package/dest/client/interface.d.ts +40 -9
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/p2p_client.d.ts +23 -12
- package/dest/client/p2p_client.d.ts.map +1 -1
- package/dest/client/p2p_client.js +38 -35
- package/dest/config.d.ts +19 -13
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +27 -26
- package/dest/errors/reqresp.error.d.ts +13 -1
- package/dest/errors/reqresp.error.d.ts.map +1 -1
- package/dest/errors/reqresp.error.js +14 -0
- 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 +9 -2
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
- package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +43 -0
- package/dest/mem_pools/instrumentation.d.ts +9 -4
- package/dest/mem_pools/instrumentation.d.ts.map +1 -1
- package/dest/mem_pools/instrumentation.js +11 -8
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts +1 -5
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/archive/tx_archive.js +1 -7
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts +3 -3
- package/dest/mem_pools/tx_pool_v2/deleted_pool.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/deleted_pool.js +6 -6
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.js +3 -3
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.js +3 -1
- package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.js +3 -1
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.js +3 -4
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.js +4 -5
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.js +6 -5
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts +30 -5
- package/dest/mem_pools/tx_pool_v2/interfaces.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/interfaces.js +2 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts +15 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_indices.js +31 -0
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts +9 -3
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2.js +19 -8
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts +17 -4
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.d.ts.map +1 -1
- package/dest/mem_pools/tx_pool_v2/tx_pool_v2_impl.js +133 -34
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +6 -5
- package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
- package/dest/msg_validators/attestation_validator/attestation_validator.js +24 -26
- package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +4 -4
- 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/proposal_validator/block_proposal_validator.d.ts +4 -3
- package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/block_proposal_validator.js +2 -2
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +4 -3
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +8 -2
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +19 -5
- package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -1
- package/dest/msg_validators/proposal_validator/proposal_validator.js +66 -30
- package/dest/msg_validators/tx_validator/factory.d.ts +9 -11
- package/dest/msg_validators/tx_validator/factory.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/factory.js +15 -6
- package/dest/msg_validators/tx_validator/gas_validator.d.ts +12 -7
- package/dest/msg_validators/tx_validator/gas_validator.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/gas_validator.js +15 -15
- package/dest/msg_validators/tx_validator/phases_validator.js +2 -2
- package/dest/msg_validators/tx_validator/timestamp_validator.js +1 -1
- package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts +2 -2
- package/dest/msg_validators/tx_validator/tx_validation_cache.d.ts.map +1 -1
- package/dest/msg_validators/tx_validator/tx_validation_cache.js +6 -7
- package/dest/services/discv5/discV5_service.d.ts +12 -2
- package/dest/services/discv5/discV5_service.d.ts.map +1 -1
- package/dest/services/discv5/discV5_service.js +84 -15
- package/dest/services/discv5/persisted_enr_store.d.ts +38 -0
- package/dest/services/discv5/persisted_enr_store.d.ts.map +1 -0
- package/dest/services/discv5/persisted_enr_store.js +145 -0
- package/dest/services/dummy_service.d.ts +6 -2
- package/dest/services/dummy_service.d.ts.map +1 -1
- package/dest/services/dummy_service.js +3 -0
- package/dest/services/encoding.d.ts +19 -6
- package/dest/services/encoding.d.ts.map +1 -1
- package/dest/services/encoding.js +25 -15
- package/dest/services/gossipsub/topic_score_params.d.ts +6 -23
- package/dest/services/gossipsub/topic_score_params.d.ts.map +1 -1
- package/dest/services/gossipsub/topic_score_params.js +4 -31
- package/dest/services/libp2p/libp2p_service.d.ts +12 -3
- package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
- package/dest/services/libp2p/libp2p_service.js +204 -114
- package/dest/services/peer-manager/peer_manager.d.ts +15 -2
- package/dest/services/peer-manager/peer_manager.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_manager.js +46 -18
- package/dest/services/peer-manager/peer_scoring.d.ts +28 -1
- package/dest/services/peer-manager/peer_scoring.d.ts.map +1 -1
- package/dest/services/peer-manager/peer_scoring.js +70 -3
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts +1 -8
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.d.ts.map +1 -1
- package/dest/services/reqresp/batch-tx-requester/batch_tx_requester.js +21 -39
- package/dest/services/reqresp/config.d.ts +13 -1
- package/dest/services/reqresp/config.d.ts.map +1 -1
- package/dest/services/reqresp/config.js +11 -0
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts +4 -3
- package/dest/services/reqresp/protocols/block_txs/bitvector.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block_txs/bitvector.js +3 -2
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts +1 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_handler.js +29 -28
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts +12 -7
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/protocols/block_txs/block_txs_reqresp.js +42 -24
- package/dest/services/reqresp/rate-limiter/rate_limits.js +3 -3
- package/dest/services/reqresp/reqresp.d.ts +1 -1
- package/dest/services/reqresp/reqresp.d.ts.map +1 -1
- package/dest/services/reqresp/reqresp.js +34 -3
- package/dest/services/service.d.ts +21 -1
- package/dest/services/service.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_source.d.ts +1 -1
- package/dest/services/tx_collection/tx_source.d.ts.map +1 -1
- package/dest/services/tx_collection/tx_source.js +4 -1
- package/dest/test-helpers/testbench-utils.d.ts +4 -1
- package/dest/test-helpers/testbench-utils.d.ts.map +1 -1
- package/dest/test-helpers/testbench-utils.js +11 -3
- package/dest/testbench/p2p_client_testbench_worker.d.ts +1 -1
- package/dest/testbench/p2p_client_testbench_worker.d.ts.map +1 -1
- package/dest/testbench/p2p_client_testbench_worker.js +19 -2
- package/package.json +15 -17
- package/src/bootstrap/bootstrap.ts +15 -9
- package/src/client/factory.ts +9 -5
- package/src/client/interface.ts +33 -7
- package/src/client/p2p_client.ts +46 -48
- package/src/config.ts +50 -38
- package/src/errors/reqresp.error.ts +19 -0
- package/src/mem_pools/attestation_pool/attestation_pool.ts +12 -2
- package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +40 -1
- package/src/mem_pools/instrumentation.ts +13 -11
- package/src/mem_pools/tx_pool_v2/archive/tx_archive.ts +1 -9
- package/src/mem_pools/tx_pool_v2/deleted_pool.ts +7 -7
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_eviction_rule.ts +2 -4
- package/src/mem_pools/tx_pool_v2/eviction/fee_payer_balance_pre_add_rule.ts +3 -2
- package/src/mem_pools/tx_pool_v2/eviction/insufficient_fee_per_gas_eviction_rule.ts +3 -1
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_mining_rule.ts +1 -3
- package/src/mem_pools/tx_pool_v2/eviction/invalid_txs_after_reorg_rule.ts +2 -4
- package/src/mem_pools/tx_pool_v2/eviction/low_priority_eviction_rule.ts +5 -6
- package/src/mem_pools/tx_pool_v2/interfaces.ts +28 -4
- package/src/mem_pools/tx_pool_v2/tx_pool_indices.ts +35 -0
- package/src/mem_pools/tx_pool_v2/tx_pool_v2.ts +22 -8
- package/src/mem_pools/tx_pool_v2/tx_pool_v2_impl.ts +158 -33
- package/src/msg_validators/attestation_validator/attestation_validator.ts +26 -26
- package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +4 -3
- package/src/msg_validators/proposal_validator/block_proposal_validator.ts +4 -2
- package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +10 -2
- package/src/msg_validators/proposal_validator/proposal_validator.ts +69 -30
- package/src/msg_validators/tx_validator/README.md +2 -0
- package/src/msg_validators/tx_validator/factory.ts +19 -13
- package/src/msg_validators/tx_validator/gas_validator.ts +16 -20
- package/src/msg_validators/tx_validator/phases_validator.ts +2 -2
- package/src/msg_validators/tx_validator/timestamp_validator.ts +1 -1
- package/src/msg_validators/tx_validator/tx_validation_cache.ts +6 -7
- package/src/services/discv5/discV5_service.ts +88 -9
- package/src/services/discv5/persisted_enr_store.ts +158 -0
- package/src/services/dummy_service.ts +6 -0
- package/src/services/encoding.ts +28 -20
- package/src/services/gossipsub/README.md +33 -11
- package/src/services/gossipsub/topic_score_params.ts +9 -50
- package/src/services/libp2p/libp2p_service.ts +234 -125
- package/src/services/peer-manager/peer_manager.ts +48 -17
- package/src/services/peer-manager/peer_scoring.ts +75 -3
- package/src/services/reqresp/README.md +2 -23
- package/src/services/reqresp/batch-tx-requester/batch_tx_requester.ts +21 -45
- package/src/services/reqresp/config.ts +17 -0
- package/src/services/reqresp/protocols/block_txs/bitvector.ts +3 -2
- package/src/services/reqresp/protocols/block_txs/block_txs_handler.ts +33 -31
- package/src/services/reqresp/protocols/block_txs/block_txs_reqresp.ts +47 -20
- package/src/services/reqresp/rate-limiter/rate_limits.ts +3 -3
- package/src/services/reqresp/reqresp.ts +42 -2
- package/src/services/service.ts +23 -0
- package/src/services/tx_collection/tx_source.ts +2 -1
- package/src/test-helpers/testbench-utils.ts +13 -2
- package/src/testbench/p2p_client_testbench_worker.ts +22 -2
- package/dest/msg_validators/clock_tolerance.d.ts +0 -32
- package/dest/msg_validators/clock_tolerance.d.ts.map +0 -1
- package/dest/msg_validators/clock_tolerance.js +0 -95
- package/src/msg_validators/clock_tolerance.ts +0 -127
|
@@ -9,7 +9,7 @@ export const DEFAULT_RATE_LIMITS: ReqRespSubProtocolRateLimits = {
|
|
|
9
9
|
},
|
|
10
10
|
globalLimit: {
|
|
11
11
|
quotaTimeMs: 1000,
|
|
12
|
-
quotaCount:
|
|
12
|
+
quotaCount: 50,
|
|
13
13
|
},
|
|
14
14
|
},
|
|
15
15
|
[ReqRespSubProtocol.STATUS]: {
|
|
@@ -19,7 +19,7 @@ export const DEFAULT_RATE_LIMITS: ReqRespSubProtocolRateLimits = {
|
|
|
19
19
|
},
|
|
20
20
|
globalLimit: {
|
|
21
21
|
quotaTimeMs: 1000,
|
|
22
|
-
quotaCount:
|
|
22
|
+
quotaCount: 50,
|
|
23
23
|
},
|
|
24
24
|
},
|
|
25
25
|
[ReqRespSubProtocol.AUTH]: {
|
|
@@ -49,7 +49,7 @@ export const DEFAULT_RATE_LIMITS: ReqRespSubProtocolRateLimits = {
|
|
|
49
49
|
},
|
|
50
50
|
globalLimit: {
|
|
51
51
|
quotaTimeMs: 1000,
|
|
52
|
-
quotaCount:
|
|
52
|
+
quotaCount: 50,
|
|
53
53
|
},
|
|
54
54
|
},
|
|
55
55
|
[ReqRespSubProtocol.BLOCK_TXS]: {
|
|
@@ -10,12 +10,18 @@ import type { Libp2p } from 'libp2p';
|
|
|
10
10
|
import { pipeline } from 'node:stream/promises';
|
|
11
11
|
import type { Uint8ArrayList } from 'uint8arraylist';
|
|
12
12
|
|
|
13
|
-
import { IndividualReqRespTimeoutError } from '../../errors/reqresp.error.js';
|
|
14
|
-
import {
|
|
13
|
+
import { IndividualReqRespTimeoutError, OversizedReqRespRequestError } from '../../errors/reqresp.error.js';
|
|
14
|
+
import {
|
|
15
|
+
DEFAULT_MAX_RESPONSE_SIZE_KB,
|
|
16
|
+
OversizedSnappyResponseError,
|
|
17
|
+
ResponseSizeLimitExceededError,
|
|
18
|
+
SnappyTransform,
|
|
19
|
+
} from '../encoding.js';
|
|
15
20
|
import type { PeerScoring } from '../peer-manager/peer_scoring.js';
|
|
16
21
|
import {
|
|
17
22
|
DEFAULT_INDIVIDUAL_REQUEST_TIMEOUT_MS,
|
|
18
23
|
DEFAULT_REQRESP_DIAL_TIMEOUT_MS,
|
|
24
|
+
MAX_REQRESP_REQUEST_SIZE_BYTES,
|
|
19
25
|
type P2PReqRespConfig,
|
|
20
26
|
} from './config.js';
|
|
21
27
|
import { ConnectionSampler, RandomSampler } from './connection-sampler/connection_sampler.js';
|
|
@@ -207,6 +213,11 @@ export class ReqResp implements ReqRespInterface {
|
|
|
207
213
|
payload: Buffer,
|
|
208
214
|
dialTimeout: number = this.dialTimeoutMs,
|
|
209
215
|
): Promise<ReqRespResponse> {
|
|
216
|
+
// Thrown before the try block so the generic error handling below does not penalize the peer for our own bug.
|
|
217
|
+
if (payload.length > MAX_REQRESP_REQUEST_SIZE_BYTES) {
|
|
218
|
+
throw new OversizedReqRespRequestError(subProtocol, payload.length, MAX_REQRESP_REQUEST_SIZE_BYTES);
|
|
219
|
+
}
|
|
220
|
+
|
|
210
221
|
let stream: Stream | undefined;
|
|
211
222
|
try {
|
|
212
223
|
this.metrics.recordRequestSent(subProtocol);
|
|
@@ -291,6 +302,14 @@ export class ReqResp implements ReqRespInterface {
|
|
|
291
302
|
* - The first chunk should contain a control byte, indicating the status of the response see `ReqRespStatus`
|
|
292
303
|
* - The second chunk should contain the response data
|
|
293
304
|
*
|
|
305
|
+
* To bound memory usage we abort reception as soon as the accumulated compressed bytes exceed a
|
|
306
|
+
* bound derived from `maxSizeKb`, before the full stream has been buffered. Without this a peer we
|
|
307
|
+
* dialed could stream data up to the request timeout, forcing us to buffer arbitrarily much (the
|
|
308
|
+
* `maxSizeKb` guard in `inboundTransformData` only runs after the whole compressed payload is
|
|
309
|
+
* concatenated, so it does nothing to cap reception memory). Snappy can expand incompressible input
|
|
310
|
+
* to ~1.17x its size, so we allow 2x `maxSizeKb`: comfortably above the worst-case expansion of a
|
|
311
|
+
* legitimate max-size response, while still capping buffered memory at twice the permitted size.
|
|
312
|
+
*
|
|
294
313
|
* @param source - The async iterable source of data chunks
|
|
295
314
|
* @param maxSizeKb - Optional maximum expected size in KB for the decompressed response
|
|
296
315
|
*/
|
|
@@ -298,10 +317,20 @@ export class ReqResp implements ReqRespInterface {
|
|
|
298
317
|
let status: ReqRespStatus | undefined;
|
|
299
318
|
const chunks: Uint8Array[] = [];
|
|
300
319
|
|
|
320
|
+
const maxAccumulatedBytes = (maxSizeKb ?? DEFAULT_MAX_RESPONSE_SIZE_KB) * 1024 * 2;
|
|
321
|
+
let accumulatedBytes = 0;
|
|
322
|
+
|
|
301
323
|
try {
|
|
302
324
|
for await (const chunk of source) {
|
|
303
325
|
const statusParsed = status !== undefined;
|
|
304
326
|
if (statusParsed) {
|
|
327
|
+
// Use `byteLength` (the logical length) rather than `chunk.subarray().length`: calling
|
|
328
|
+
// `subarray()` on a `Uint8ArrayList` consolidates its backing buffers into a single copy,
|
|
329
|
+
// which would materialize the very chunk we are trying to reject.
|
|
330
|
+
accumulatedBytes += chunk.byteLength;
|
|
331
|
+
if (accumulatedBytes > maxAccumulatedBytes) {
|
|
332
|
+
throw new ResponseSizeLimitExceededError(accumulatedBytes, maxAccumulatedBytes);
|
|
333
|
+
}
|
|
305
334
|
chunks.push(chunk.subarray());
|
|
306
335
|
continue;
|
|
307
336
|
}
|
|
@@ -440,6 +469,9 @@ export class ReqResp implements ReqRespInterface {
|
|
|
440
469
|
await pipeline(
|
|
441
470
|
stream.source,
|
|
442
471
|
async function* (source: any) {
|
|
472
|
+
// Req/resp is one-request-one-response: an honest sender writes a single payload and half-closes.
|
|
473
|
+
// Process only the first chunk and return, so extra frames a peer queues on the same stream cannot
|
|
474
|
+
// drive further handler invocations that bypass the per-stream rate-limit check in streamHandler.
|
|
443
475
|
for await (const chunk of source) {
|
|
444
476
|
const response = await handler(connection.remotePeer, chunk.subarray());
|
|
445
477
|
|
|
@@ -454,6 +486,7 @@ export class ReqResp implements ReqRespInterface {
|
|
|
454
486
|
|
|
455
487
|
yield SUCCESS;
|
|
456
488
|
yield snappy.outboundTransformData(response);
|
|
489
|
+
return;
|
|
457
490
|
}
|
|
458
491
|
},
|
|
459
492
|
stream.sink,
|
|
@@ -564,6 +597,13 @@ export class ReqResp implements ReqRespInterface {
|
|
|
564
597
|
return PeerErrorSeverity.LowToleranceError;
|
|
565
598
|
}
|
|
566
599
|
|
|
600
|
+
// Response exceeded the reception buffer bound: the peer streamed more compressed data than the
|
|
601
|
+
// per-request size limit allows. Same protocol violation as above, penalize harshly.
|
|
602
|
+
if (e instanceof ResponseSizeLimitExceededError) {
|
|
603
|
+
this.logger.warn(`Oversized response from peer ${peerId.toString()} in ${subProtocol}: ${e.message}`, logTags);
|
|
604
|
+
return PeerErrorSeverity.LowToleranceError;
|
|
605
|
+
}
|
|
606
|
+
|
|
567
607
|
return this.categorizeConnectionErrors(e, peerId, subProtocol);
|
|
568
608
|
}
|
|
569
609
|
|
package/src/services/service.ts
CHANGED
|
@@ -58,6 +58,19 @@ export type DuplicateProposalInfo = {
|
|
|
58
58
|
*/
|
|
59
59
|
export type P2PDuplicateProposalCallback = (info: DuplicateProposalInfo) => void;
|
|
60
60
|
|
|
61
|
+
/** Minimal info passed to the oversized proposal callback. */
|
|
62
|
+
export type OversizedProposalInfo = {
|
|
63
|
+
slot: SlotNumber;
|
|
64
|
+
proposer: EthAddress;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Callback for when a block proposal whose index lands at or beyond the consensus per-checkpoint block
|
|
69
|
+
* limit is stored and re-broadcast as slashing evidence. May fire multiple times per (slot, proposer)
|
|
70
|
+
* if the proposer signed several oversized proposals; consumers are expected to dedup.
|
|
71
|
+
*/
|
|
72
|
+
export type P2POversizedProposalCallback = (info: OversizedProposalInfo) => void;
|
|
73
|
+
|
|
61
74
|
/** Minimal info passed to the duplicate attestation callback. */
|
|
62
75
|
export type DuplicateAttestationInfo = {
|
|
63
76
|
slot: SlotNumber;
|
|
@@ -108,6 +121,12 @@ export interface P2PService {
|
|
|
108
121
|
*/
|
|
109
122
|
registerDuplicateProposalCallback(callback: P2PDuplicateProposalCallback): void;
|
|
110
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Registers a callback invoked when an oversized block proposal (index at or beyond the consensus
|
|
126
|
+
* per-checkpoint block limit) is stored and re-broadcast as slashing evidence.
|
|
127
|
+
*/
|
|
128
|
+
registerOversizedProposalCallback(callback: P2POversizedProposalCallback): void;
|
|
129
|
+
|
|
111
130
|
/**
|
|
112
131
|
* Registers a callback invoked when a duplicate attestation is detected (equivocation).
|
|
113
132
|
* A validator signing attestations for different proposals at the same slot.
|
|
@@ -124,6 +143,10 @@ export interface P2PService {
|
|
|
124
143
|
/** Returns the number of peers in the GossipSub mesh for a given topic type. */
|
|
125
144
|
getGossipMeshPeerCount(topicType: TopicType): number;
|
|
126
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Runs minimum integrity validation on txs carried in a block proposal.
|
|
148
|
+
* @throws InvalidBlockProposalTxsError - If any tx fails validation.
|
|
149
|
+
*/
|
|
127
150
|
validateTxsReceivedInBlockProposal(txs: Tx[]): Promise<void>;
|
|
128
151
|
|
|
129
152
|
addReqRespSubProtocol(subProtocol: ReqRespSubProtocol, handler: ReqRespSubProtocolHandler): Promise<void>;
|
|
@@ -30,7 +30,8 @@ export class NodeRpcTxSource implements TxSource {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
public async getTxsByHash(txHashes: TxHash[]): Promise<TxSourceCollectionResult> {
|
|
33
|
-
|
|
33
|
+
// Collected txs feed block validation and proving, so we need their proofs.
|
|
34
|
+
return this.verifyTxs(await this.client.getTxsByHash(txHashes, { includeProof: true }));
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
private async verifyTxs(txs: Tx[]): Promise<TxSourceCollectionResult> {
|
|
@@ -119,6 +119,10 @@ export class InMemoryTxPool extends EventEmitter implements TxPoolV2 {
|
|
|
119
119
|
return Promise.resolve();
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
unprotectTxs(_txHashes: TxHash[], _slotNumber: SlotNumber): Promise<void> {
|
|
123
|
+
return Promise.resolve();
|
|
124
|
+
}
|
|
125
|
+
|
|
122
126
|
handlePrunedBlocks(_latestBlock: L2BlockId, _options?: { deleteAllTxs?: boolean }): Promise<void> {
|
|
123
127
|
return Promise.resolve();
|
|
124
128
|
}
|
|
@@ -167,6 +171,10 @@ export class InMemoryTxPool extends EventEmitter implements TxPoolV2 {
|
|
|
167
171
|
return Promise.resolve(this.txsByHash.size);
|
|
168
172
|
}
|
|
169
173
|
|
|
174
|
+
async hasEligiblePendingTxs(minCount: number): Promise<boolean> {
|
|
175
|
+
return (await this.getPendingTxCount()) >= minCount;
|
|
176
|
+
}
|
|
177
|
+
|
|
170
178
|
getMinedTxHashes(): Promise<[TxHash, L2BlockId][]> {
|
|
171
179
|
return Promise.resolve([]);
|
|
172
180
|
}
|
|
@@ -237,6 +245,10 @@ export class InMemoryAttestationPool {
|
|
|
237
245
|
return Promise.resolve(this.checkpoints.get(slot)?.[0]);
|
|
238
246
|
}
|
|
239
247
|
|
|
248
|
+
hasCheckpointProposalForSlot(slot: SlotNumber): Promise<boolean> {
|
|
249
|
+
return Promise.resolve(this.checkpoints.has(slot));
|
|
250
|
+
}
|
|
251
|
+
|
|
240
252
|
getProposalsForSlot(slot: SlotNumber): Promise<{
|
|
241
253
|
blockProposals: BlockProposal[];
|
|
242
254
|
checkpointProposals: CheckpointProposalCore[];
|
|
@@ -297,8 +309,6 @@ export function createMockEpochCache(): EpochCacheInterface {
|
|
|
297
309
|
ts: 0n,
|
|
298
310
|
nowMs: 0n,
|
|
299
311
|
}),
|
|
300
|
-
isProposerPipeliningEnabled: () => false,
|
|
301
|
-
pipeliningOffset: () => 0,
|
|
302
312
|
computeProposerIndex: () => 0n,
|
|
303
313
|
getCurrentAndNextSlot: () => ({ currentSlot: SlotNumber.ZERO, nextSlot: SlotNumber.ZERO }),
|
|
304
314
|
getTargetAndNextSlot: () => ({ targetSlot: SlotNumber.ZERO, nextSlot: SlotNumber.ZERO }),
|
|
@@ -330,6 +340,7 @@ export function createMockEpochCache(): EpochCacheInterface {
|
|
|
330
340
|
targetCommitteeSize: 48,
|
|
331
341
|
rollupManaLimit: Number.MAX_SAFE_INTEGER,
|
|
332
342
|
}),
|
|
343
|
+
getLagInEpochsForValidatorSet: () => 2,
|
|
333
344
|
};
|
|
334
345
|
return cache;
|
|
335
346
|
}
|
|
@@ -17,6 +17,7 @@ import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
|
|
|
17
17
|
import { protocolContractsHash } from '@aztec/protocol-contracts';
|
|
18
18
|
import type { L2BlockSource } from '@aztec/stdlib/block';
|
|
19
19
|
import type { ContractDataSource } from '@aztec/stdlib/contract';
|
|
20
|
+
import { EmptyL1RollupConstants, type L1RollupConstants } from '@aztec/stdlib/epoch-helpers';
|
|
20
21
|
import { GasFees } from '@aztec/stdlib/gas';
|
|
21
22
|
import type { ClientProtocolCircuitVerifier, WorldStateSynchronizer } from '@aztec/stdlib/interfaces/server';
|
|
22
23
|
import type { DataStoreConfig } from '@aztec/stdlib/kv-store';
|
|
@@ -84,6 +85,25 @@ export interface BenchReadyMessage {
|
|
|
84
85
|
}
|
|
85
86
|
const txCache = new Map<number, Tx[]>();
|
|
86
87
|
|
|
88
|
+
const BENCH_L1_CONSTANTS: L1RollupConstants = {
|
|
89
|
+
...EmptyL1RollupConstants,
|
|
90
|
+
slotDuration: 12,
|
|
91
|
+
ethereumSlotDuration: 4,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
class TestbenchL2BlockSource extends MockL2BlockSource {
|
|
95
|
+
public override getL1Constants(): Promise<L1RollupConstants> {
|
|
96
|
+
return Promise.resolve(BENCH_L1_CONSTANTS);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function createBenchmarkEpochCache(): EpochCacheInterface {
|
|
101
|
+
return {
|
|
102
|
+
...createMockEpochCache(),
|
|
103
|
+
getL1Constants: () => BENCH_L1_CONSTANTS,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
87
107
|
class TestLibP2PService extends LibP2PService {
|
|
88
108
|
private disableTxValidation: boolean;
|
|
89
109
|
private gossipMessageCount = 0;
|
|
@@ -370,9 +390,9 @@ process.on('message', async msg => {
|
|
|
370
390
|
workerConfig = config;
|
|
371
391
|
workerTxPool = new InMemoryTxPool();
|
|
372
392
|
workerAttestationPool = new InMemoryAttestationPool();
|
|
373
|
-
const epochCache =
|
|
393
|
+
const epochCache = createBenchmarkEpochCache();
|
|
374
394
|
const worldState = createMockWorldStateSynchronizer();
|
|
375
|
-
const l2BlockSource = new
|
|
395
|
+
const l2BlockSource = new TestbenchL2BlockSource();
|
|
376
396
|
|
|
377
397
|
const proofVerifier = new AlwaysTrueCircuitVerifier();
|
|
378
398
|
kvStore = await openTmpStore(`test-${clientIndex}`, true, BENCHMARK_CONSTANTS.KV_STORE_MAP_SIZE_KB);
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
2
|
-
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
3
|
-
/**
|
|
4
|
-
* Maximum clock disparity tolerance for P2P message validation (in milliseconds).
|
|
5
|
-
* Messages for the previous slot are accepted if we're within this many milliseconds
|
|
6
|
-
* of the current slot start. This prevents penalizing peers for messages that
|
|
7
|
-
* were valid when sent but arrived slightly late due to network latency.
|
|
8
|
-
*
|
|
9
|
-
* This follows Ethereum's MAXIMUM_GOSSIP_CLOCK_DISPARITY approach.
|
|
10
|
-
*/
|
|
11
|
-
export declare const MAXIMUM_GOSSIP_CLOCK_DISPARITY_MS = 500;
|
|
12
|
-
/**
|
|
13
|
-
* Checks if a message for the previous slot should be accepted due to clock tolerance.
|
|
14
|
-
*
|
|
15
|
-
* @param messageSlot - The slot number from the received message
|
|
16
|
-
* @param currentSlot - The current slot number
|
|
17
|
-
* @param epochCache - EpochCache to get timing information
|
|
18
|
-
* @returns true if the message is for the previous slot AND we're within the clock tolerance window
|
|
19
|
-
*/
|
|
20
|
-
export declare function isWithinClockTolerance(messageSlot: SlotNumber, currentSlot: SlotNumber, epochCache: EpochCacheInterface): boolean;
|
|
21
|
-
export declare class PipeliningWindow {
|
|
22
|
-
private readonly epochCache;
|
|
23
|
-
private readonly proposalWindowIntoTargetSlot;
|
|
24
|
-
private readonly attestationWindowIntoTargetSlot;
|
|
25
|
-
constructor(epochCache: EpochCacheInterface, opts?: {
|
|
26
|
-
p2pPropagationTime?: number;
|
|
27
|
-
l1PublishingTime?: number;
|
|
28
|
-
});
|
|
29
|
-
acceptsProposal(messageSlot: SlotNumber): boolean;
|
|
30
|
-
acceptsAttestation(messageSlot: SlotNumber): boolean;
|
|
31
|
-
}
|
|
32
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xvY2tfdG9sZXJhbmNlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvbXNnX3ZhbGlkYXRvcnMvY2xvY2tfdG9sZXJhbmNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDOUQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBRzdEOzs7Ozs7O0dBT0c7QUFDSCxlQUFPLE1BQU0saUNBQWlDLE1BQU0sQ0FBQztBQUVyRDs7Ozs7OztHQU9HO0FBQ0gsd0JBQWdCLHNCQUFzQixDQUNwQyxXQUFXLEVBQUUsVUFBVSxFQUN2QixXQUFXLEVBQUUsVUFBVSxFQUN2QixVQUFVLEVBQUUsbUJBQW1CLEdBQzlCLE9BQU8sQ0EwQlQ7QUE0Q0QscUJBQWEsZ0JBQWdCO0lBS3pCLE9BQU8sQ0FBQyxRQUFRLENBQUMsVUFBVTtJQUo3QixPQUFPLENBQUMsUUFBUSxDQUFDLDRCQUE0QixDQUFTO0lBQ3RELE9BQU8sQ0FBQyxRQUFRLENBQUMsK0JBQStCLENBQVM7SUFFekQsWUFDbUIsVUFBVSxFQUFFLG1CQUFtQixFQUNoRCxJQUFJLEdBQUU7UUFDSixrQkFBa0IsQ0FBQyxFQUFFLE1BQU0sQ0FBQztRQUM1QixnQkFBZ0IsQ0FBQyxFQUFFLE1BQU0sQ0FBQztLQUN0QixFQVlQO0lBRU0sZUFBZSxDQUFDLFdBQVcsRUFBRSxVQUFVLEdBQUcsT0FBTyxDQUV2RDtJQUVNLGtCQUFrQixDQUFDLFdBQVcsRUFBRSxVQUFVLEdBQUcsT0FBTyxDQUUxRDtDQUNGIn0=
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"clock_tolerance.d.ts","sourceRoot":"","sources":["../../src/msg_validators/clock_tolerance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAG7D;;;;;;;GAOG;AACH,eAAO,MAAM,iCAAiC,MAAM,CAAC;AAErD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,UAAU,EACvB,WAAW,EAAE,UAAU,EACvB,UAAU,EAAE,mBAAmB,GAC9B,OAAO,CA0BT;AA4CD,qBAAa,gBAAgB;IAKzB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAJ7B,OAAO,CAAC,QAAQ,CAAC,4BAA4B,CAAS;IACtD,OAAO,CAAC,QAAQ,CAAC,+BAA+B,CAAS;IAEzD,YACmB,UAAU,EAAE,mBAAmB,EAChD,IAAI,GAAE;QACJ,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;KACtB,EAYP;IAEM,eAAe,CAAC,WAAW,EAAE,UAAU,GAAG,OAAO,CAEvD;IAEM,kBAAkB,CAAC,WAAW,EAAE,UAAU,GAAG,OAAO,CAE1D;CACF"}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
2
|
-
import { DEFAULT_P2P_PROPAGATION_TIME, createPipelinedCheckpointTimingModel } from '@aztec/stdlib/timetable';
|
|
3
|
-
/**
|
|
4
|
-
* Maximum clock disparity tolerance for P2P message validation (in milliseconds).
|
|
5
|
-
* Messages for the previous slot are accepted if we're within this many milliseconds
|
|
6
|
-
* of the current slot start. This prevents penalizing peers for messages that
|
|
7
|
-
* were valid when sent but arrived slightly late due to network latency.
|
|
8
|
-
*
|
|
9
|
-
* This follows Ethereum's MAXIMUM_GOSSIP_CLOCK_DISPARITY approach.
|
|
10
|
-
*/ export const MAXIMUM_GOSSIP_CLOCK_DISPARITY_MS = 500;
|
|
11
|
-
/**
|
|
12
|
-
* Checks if a message for the previous slot should be accepted due to clock tolerance.
|
|
13
|
-
*
|
|
14
|
-
* @param messageSlot - The slot number from the received message
|
|
15
|
-
* @param currentSlot - The current slot number
|
|
16
|
-
* @param epochCache - EpochCache to get timing information
|
|
17
|
-
* @returns true if the message is for the previous slot AND we're within the clock tolerance window
|
|
18
|
-
*/ export function isWithinClockTolerance(messageSlot, currentSlot, epochCache) {
|
|
19
|
-
// Guard against slot 0 edge case (genesis)
|
|
20
|
-
if (currentSlot === SlotNumber.ZERO) {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
// Only apply tolerance to messages for the previous slot
|
|
24
|
-
const previousSlot = SlotNumber(currentSlot - 1);
|
|
25
|
-
if (messageSlot !== previousSlot) {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
// Check how far we are into the current slot (in milliseconds)
|
|
29
|
-
const { ts: slotStartTs, nowMs } = epochCache.getEpochAndSlotNow();
|
|
30
|
-
const targetSlot = epochCache.getTargetSlot();
|
|
31
|
-
// Sanity check: ensure the epoch cache's target slot matches the expected current slot
|
|
32
|
-
if (targetSlot !== currentSlot) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
// ts is in seconds, convert to ms; nowMs is already in milliseconds
|
|
36
|
-
const slotStartMs = slotStartTs * 1000n;
|
|
37
|
-
const elapsedMs = Number(nowMs - slotStartMs);
|
|
38
|
-
return elapsedMs < MAXIMUM_GOSSIP_CLOCK_DISPARITY_MS;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Checks if a straggler message for the previous target slot should be accepted.
|
|
42
|
-
*
|
|
43
|
-
* Under pipelining, proposals and attestations carry the target slot N. Most of the
|
|
44
|
-
* time the receiver is either still in the build slot N-1 (accepted via the main
|
|
45
|
-
* `slotNumber === targetSlot` match) or in the target slot N (accepted via
|
|
46
|
-
* `slotNumber === nextSlot` when pipelining is disabled, or again via `targetSlot`
|
|
47
|
-
* when the receiver itself is pipelining). Stragglers that arrive after the receiver
|
|
48
|
-
* has rolled past the target slot fall to this check: accept `messageSlot === slotNow`
|
|
49
|
-
* while we're still within the first `windowSeconds + clock-disparity` of the slot.
|
|
50
|
-
*
|
|
51
|
-
* Under the early-pipelining schedule `windowSeconds` is small (0 for proposals,
|
|
52
|
-
* `2*p2pPropagationTime` for attestations) since the proposer collects everything
|
|
53
|
-
* before the slot boundary.
|
|
54
|
-
*
|
|
55
|
-
* @param messageSlot - The slot number from the received message
|
|
56
|
-
* @param epochCache - EpochCache to get timing and pipelining state
|
|
57
|
-
* @param windowSeconds - How far into the current slot we still accept previous-target messages
|
|
58
|
-
* @returns true if pipelining is enabled, the message is for the current wallclock slot, and we're within the grace period
|
|
59
|
-
*/ function isWithinPipeliningWindow(messageSlot, epochCache, windowSeconds) {
|
|
60
|
-
if (!epochCache.isProposerPipeliningEnabled()) {
|
|
61
|
-
return false;
|
|
62
|
-
}
|
|
63
|
-
const currentSlot = epochCache.getSlotNow();
|
|
64
|
-
if (messageSlot !== currentSlot) {
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
const { ts: slotStartTs, nowMs } = epochCache.getEpochAndSlotNow();
|
|
68
|
-
const slotStartMs = slotStartTs * 1000n;
|
|
69
|
-
const elapsedMs = Number(nowMs - slotStartMs);
|
|
70
|
-
const windowMs = windowSeconds * 1000 + MAXIMUM_GOSSIP_CLOCK_DISPARITY_MS;
|
|
71
|
-
return elapsedMs < windowMs;
|
|
72
|
-
}
|
|
73
|
-
export class PipeliningWindow {
|
|
74
|
-
epochCache;
|
|
75
|
-
proposalWindowIntoTargetSlot;
|
|
76
|
-
attestationWindowIntoTargetSlot;
|
|
77
|
-
constructor(epochCache, opts = {}){
|
|
78
|
-
this.epochCache = epochCache;
|
|
79
|
-
const l1Constants = epochCache.getL1Constants();
|
|
80
|
-
const checkpointTiming = createPipelinedCheckpointTimingModel({
|
|
81
|
-
aztecSlotDuration: l1Constants.slotDuration,
|
|
82
|
-
ethereumSlotDuration: l1Constants.ethereumSlotDuration,
|
|
83
|
-
l1PublishingTime: opts.l1PublishingTime ?? l1Constants.ethereumSlotDuration,
|
|
84
|
-
p2pPropagationTime: opts.p2pPropagationTime ?? DEFAULT_P2P_PROPAGATION_TIME
|
|
85
|
-
});
|
|
86
|
-
this.proposalWindowIntoTargetSlot = checkpointTiming.proposalWindowIntoTargetSlot;
|
|
87
|
-
this.attestationWindowIntoTargetSlot = checkpointTiming.attestationWindowIntoTargetSlot;
|
|
88
|
-
}
|
|
89
|
-
acceptsProposal(messageSlot) {
|
|
90
|
-
return isWithinPipeliningWindow(messageSlot, this.epochCache, this.proposalWindowIntoTargetSlot);
|
|
91
|
-
}
|
|
92
|
-
acceptsAttestation(messageSlot) {
|
|
93
|
-
return isWithinPipeliningWindow(messageSlot, this.epochCache, this.attestationWindowIntoTargetSlot);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import type { EpochCacheInterface } from '@aztec/epoch-cache';
|
|
2
|
-
import { SlotNumber } from '@aztec/foundation/branded-types';
|
|
3
|
-
import { DEFAULT_P2P_PROPAGATION_TIME, createPipelinedCheckpointTimingModel } from '@aztec/stdlib/timetable';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Maximum clock disparity tolerance for P2P message validation (in milliseconds).
|
|
7
|
-
* Messages for the previous slot are accepted if we're within this many milliseconds
|
|
8
|
-
* of the current slot start. This prevents penalizing peers for messages that
|
|
9
|
-
* were valid when sent but arrived slightly late due to network latency.
|
|
10
|
-
*
|
|
11
|
-
* This follows Ethereum's MAXIMUM_GOSSIP_CLOCK_DISPARITY approach.
|
|
12
|
-
*/
|
|
13
|
-
export const MAXIMUM_GOSSIP_CLOCK_DISPARITY_MS = 500;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Checks if a message for the previous slot should be accepted due to clock tolerance.
|
|
17
|
-
*
|
|
18
|
-
* @param messageSlot - The slot number from the received message
|
|
19
|
-
* @param currentSlot - The current slot number
|
|
20
|
-
* @param epochCache - EpochCache to get timing information
|
|
21
|
-
* @returns true if the message is for the previous slot AND we're within the clock tolerance window
|
|
22
|
-
*/
|
|
23
|
-
export function isWithinClockTolerance(
|
|
24
|
-
messageSlot: SlotNumber,
|
|
25
|
-
currentSlot: SlotNumber,
|
|
26
|
-
epochCache: EpochCacheInterface,
|
|
27
|
-
): boolean {
|
|
28
|
-
// Guard against slot 0 edge case (genesis)
|
|
29
|
-
if (currentSlot === SlotNumber.ZERO) {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Only apply tolerance to messages for the previous slot
|
|
34
|
-
const previousSlot = SlotNumber(currentSlot - 1);
|
|
35
|
-
if (messageSlot !== previousSlot) {
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Check how far we are into the current slot (in milliseconds)
|
|
40
|
-
const { ts: slotStartTs, nowMs } = epochCache.getEpochAndSlotNow();
|
|
41
|
-
const targetSlot = epochCache.getTargetSlot();
|
|
42
|
-
|
|
43
|
-
// Sanity check: ensure the epoch cache's target slot matches the expected current slot
|
|
44
|
-
if (targetSlot !== currentSlot) {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// ts is in seconds, convert to ms; nowMs is already in milliseconds
|
|
49
|
-
const slotStartMs = slotStartTs * 1000n;
|
|
50
|
-
const elapsedMs = Number(nowMs - slotStartMs);
|
|
51
|
-
|
|
52
|
-
return elapsedMs < MAXIMUM_GOSSIP_CLOCK_DISPARITY_MS;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Checks if a straggler message for the previous target slot should be accepted.
|
|
57
|
-
*
|
|
58
|
-
* Under pipelining, proposals and attestations carry the target slot N. Most of the
|
|
59
|
-
* time the receiver is either still in the build slot N-1 (accepted via the main
|
|
60
|
-
* `slotNumber === targetSlot` match) or in the target slot N (accepted via
|
|
61
|
-
* `slotNumber === nextSlot` when pipelining is disabled, or again via `targetSlot`
|
|
62
|
-
* when the receiver itself is pipelining). Stragglers that arrive after the receiver
|
|
63
|
-
* has rolled past the target slot fall to this check: accept `messageSlot === slotNow`
|
|
64
|
-
* while we're still within the first `windowSeconds + clock-disparity` of the slot.
|
|
65
|
-
*
|
|
66
|
-
* Under the early-pipelining schedule `windowSeconds` is small (0 for proposals,
|
|
67
|
-
* `2*p2pPropagationTime` for attestations) since the proposer collects everything
|
|
68
|
-
* before the slot boundary.
|
|
69
|
-
*
|
|
70
|
-
* @param messageSlot - The slot number from the received message
|
|
71
|
-
* @param epochCache - EpochCache to get timing and pipelining state
|
|
72
|
-
* @param windowSeconds - How far into the current slot we still accept previous-target messages
|
|
73
|
-
* @returns true if pipelining is enabled, the message is for the current wallclock slot, and we're within the grace period
|
|
74
|
-
*/
|
|
75
|
-
function isWithinPipeliningWindow(
|
|
76
|
-
messageSlot: SlotNumber,
|
|
77
|
-
epochCache: EpochCacheInterface,
|
|
78
|
-
windowSeconds: number,
|
|
79
|
-
): boolean {
|
|
80
|
-
if (!epochCache.isProposerPipeliningEnabled()) {
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const currentSlot = epochCache.getSlotNow();
|
|
85
|
-
if (messageSlot !== currentSlot) {
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const { ts: slotStartTs, nowMs } = epochCache.getEpochAndSlotNow();
|
|
90
|
-
const slotStartMs = slotStartTs * 1000n;
|
|
91
|
-
const elapsedMs = Number(nowMs - slotStartMs);
|
|
92
|
-
const windowMs = windowSeconds * 1000 + MAXIMUM_GOSSIP_CLOCK_DISPARITY_MS;
|
|
93
|
-
|
|
94
|
-
return elapsedMs < windowMs;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export class PipeliningWindow {
|
|
98
|
-
private readonly proposalWindowIntoTargetSlot: number;
|
|
99
|
-
private readonly attestationWindowIntoTargetSlot: number;
|
|
100
|
-
|
|
101
|
-
constructor(
|
|
102
|
-
private readonly epochCache: EpochCacheInterface,
|
|
103
|
-
opts: {
|
|
104
|
-
p2pPropagationTime?: number;
|
|
105
|
-
l1PublishingTime?: number;
|
|
106
|
-
} = {},
|
|
107
|
-
) {
|
|
108
|
-
const l1Constants = epochCache.getL1Constants();
|
|
109
|
-
const checkpointTiming = createPipelinedCheckpointTimingModel({
|
|
110
|
-
aztecSlotDuration: l1Constants.slotDuration,
|
|
111
|
-
ethereumSlotDuration: l1Constants.ethereumSlotDuration,
|
|
112
|
-
l1PublishingTime: opts.l1PublishingTime ?? l1Constants.ethereumSlotDuration,
|
|
113
|
-
p2pPropagationTime: opts.p2pPropagationTime ?? DEFAULT_P2P_PROPAGATION_TIME,
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
this.proposalWindowIntoTargetSlot = checkpointTiming.proposalWindowIntoTargetSlot;
|
|
117
|
-
this.attestationWindowIntoTargetSlot = checkpointTiming.attestationWindowIntoTargetSlot;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
public acceptsProposal(messageSlot: SlotNumber): boolean {
|
|
121
|
-
return isWithinPipeliningWindow(messageSlot, this.epochCache, this.proposalWindowIntoTargetSlot);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
public acceptsAttestation(messageSlot: SlotNumber): boolean {
|
|
125
|
-
return isWithinPipeliningWindow(messageSlot, this.epochCache, this.attestationWindowIntoTargetSlot);
|
|
126
|
-
}
|
|
127
|
-
}
|