@lodestar/beacon-node 1.43.0-dev.b05ea98d04 → 1.43.0-dev.b0e1143d06
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/lib/chain/blocks/importExecutionPayload.d.ts +4 -0
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +12 -1
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +6 -7
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +8 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +19 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +16 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts +1 -3
- package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.js +29 -26
- package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +5 -4
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +15 -1
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +7 -6
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +21 -5
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +4 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +5 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +749 -2
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/interface.d.ts +1 -0
- package/lib/network/interface.d.ts.map +1 -1
- package/lib/network/network.d.ts +1 -0
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +3 -0
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +31 -1
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/reqresp/ReqRespBeaconNode.d.ts.map +1 -1
- package/lib/network/reqresp/ReqRespBeaconNode.js +1 -1
- package/lib/network/reqresp/ReqRespBeaconNode.js.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByHead.d.ts +9 -0
- package/lib/network/reqresp/handlers/beaconBlocksByHead.d.ts.map +1 -0
- package/lib/network/reqresp/handlers/beaconBlocksByHead.js +61 -0
- package/lib/network/reqresp/handlers/beaconBlocksByHead.js.map +1 -0
- package/lib/network/reqresp/handlers/index.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/index.js +5 -0
- package/lib/network/reqresp/handlers/index.js.map +1 -1
- package/lib/network/reqresp/interface.d.ts +1 -1
- package/lib/network/reqresp/interface.js +1 -1
- package/lib/network/reqresp/protocols.d.ts +1 -0
- package/lib/network/reqresp/protocols.d.ts.map +1 -1
- package/lib/network/reqresp/protocols.js +5 -0
- package/lib/network/reqresp/protocols.js.map +1 -1
- package/lib/network/reqresp/rateLimit.d.ts.map +1 -1
- package/lib/network/reqresp/rateLimit.js +4 -0
- package/lib/network/reqresp/rateLimit.js.map +1 -1
- package/lib/network/reqresp/score.d.ts.map +1 -1
- package/lib/network/reqresp/score.js +1 -0
- package/lib/network/reqresp/score.js.map +1 -1
- package/lib/network/reqresp/types.d.ts +3 -0
- package/lib/network/reqresp/types.d.ts.map +1 -1
- package/lib/network/reqresp/types.js +3 -0
- package/lib/network/reqresp/types.js.map +1 -1
- package/lib/sync/constants.d.ts +5 -1
- package/lib/sync/constants.d.ts.map +1 -1
- package/lib/sync/constants.js +5 -1
- package/lib/sync/constants.js.map +1 -1
- package/lib/sync/range/batch.d.ts +27 -6
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +149 -26
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts +10 -2
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +55 -7
- package/lib/sync/range/chain.js.map +1 -1
- package/lib/sync/range/range.d.ts.map +1 -1
- package/lib/sync/range/range.js +54 -5
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/range/utils/peerBalancer.d.ts +2 -1
- package/lib/sync/range/utils/peerBalancer.d.ts.map +1 -1
- package/lib/sync/range/utils/peerBalancer.js +8 -4
- package/lib/sync/range/utils/peerBalancer.js.map +1 -1
- package/lib/sync/sync.d.ts.map +1 -1
- package/lib/sync/sync.js +13 -0
- package/lib/sync/sync.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +7 -0
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +127 -12
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts +36 -9
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +87 -38
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/lib/sync/utils/rateLimit.d.ts +2 -0
- package/lib/sync/utils/rateLimit.d.ts.map +1 -0
- package/lib/sync/utils/rateLimit.js +15 -0
- package/lib/sync/utils/rateLimit.js.map +1 -0
- package/package.json +16 -16
- package/src/chain/blocks/importExecutionPayload.ts +18 -6
- package/src/chain/blocks/index.ts +5 -7
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +27 -1
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +17 -0
- package/src/chain/blocks/utils/chainSegment.ts +30 -27
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +7 -4
- package/src/chain/chain.ts +17 -0
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +23 -6
- package/src/metrics/metrics/lodestar.ts +6 -0
- package/src/network/interface.ts +1 -0
- package/src/network/network.ts +12 -0
- package/src/network/processor/gossipHandlers.ts +35 -1
- package/src/network/reqresp/ReqRespBeaconNode.ts +1 -0
- package/src/network/reqresp/handlers/beaconBlocksByHead.ts +91 -0
- package/src/network/reqresp/handlers/index.ts +5 -0
- package/src/network/reqresp/interface.ts +1 -1
- package/src/network/reqresp/protocols.ts +6 -0
- package/src/network/reqresp/rateLimit.ts +4 -0
- package/src/network/reqresp/score.ts +1 -0
- package/src/network/reqresp/types.ts +5 -0
- package/src/sync/constants.ts +5 -1
- package/src/sync/range/batch.ts +188 -30
- package/src/sync/range/chain.ts +72 -13
- package/src/sync/range/range.ts +68 -5
- package/src/sync/range/utils/peerBalancer.ts +9 -3
- package/src/sync/sync.ts +13 -1
- package/src/sync/unknownBlock.ts +157 -12
- package/src/sync/utils/downloadByRange.ts +153 -39
- package/src/sync/utils/rateLimit.ts +16 -0
|
@@ -8,13 +8,14 @@ import {
|
|
|
8
8
|
isForkPostGloas,
|
|
9
9
|
} from "@lodestar/params";
|
|
10
10
|
import {
|
|
11
|
+
ColumnIndex,
|
|
11
12
|
DataColumnSidecar,
|
|
13
|
+
RootHex,
|
|
12
14
|
SignedBeaconBlock,
|
|
13
15
|
Slot,
|
|
14
16
|
deneb,
|
|
15
17
|
fulu,
|
|
16
18
|
gloas,
|
|
17
|
-
isGloasBeaconBlock,
|
|
18
19
|
isGloasDataColumnSidecar,
|
|
19
20
|
phase0,
|
|
20
21
|
} from "@lodestar/types";
|
|
@@ -46,6 +47,22 @@ export type DownloadByRangeRequests = {
|
|
|
46
47
|
blobsRequest?: deneb.BlobSidecarsByRangeRequest;
|
|
47
48
|
columnsRequest?: fulu.DataColumnSidecarsByRangeRequest;
|
|
48
49
|
envelopesRequest?: gloas.ExecutionPayloadEnvelopesByRangeRequest;
|
|
50
|
+
/**
|
|
51
|
+
* Post-Gloas only. Fetches the dangling-parent's payload envelope and/or its missing sampled
|
|
52
|
+
* data columns by-root. Set by `Batch` for the first batch of a `SyncChain` after the first
|
|
53
|
+
* block's parentRoot is known.
|
|
54
|
+
*/
|
|
55
|
+
parentPayloadRequest?: {
|
|
56
|
+
blockRoot?: Uint8Array;
|
|
57
|
+
columns?: ColumnIndex[];
|
|
58
|
+
envelopeBlockRoot?: Uint8Array;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export type ParentPayloadCommitments = {
|
|
63
|
+
blockRoot: Uint8Array;
|
|
64
|
+
blockRootHex: RootHex;
|
|
65
|
+
kzgCommitments: deneb.BlobKzgCommitments;
|
|
49
66
|
};
|
|
50
67
|
|
|
51
68
|
export type DownloadByRangeResponses = {
|
|
@@ -61,6 +78,8 @@ export type DownloadAndCacheByRangeProps = DownloadByRangeRequests & {
|
|
|
61
78
|
logger: Logger;
|
|
62
79
|
peerIdStr: string;
|
|
63
80
|
batchBlocks?: IBlockInput[];
|
|
81
|
+
/** Required when `parentPayloadRequest` is set; supplies the data needed to validate the parent's columns. */
|
|
82
|
+
parentPayloadCommitments?: ParentPayloadCommitments;
|
|
64
83
|
peerDasMetrics?: BeaconMetrics["peerDas"] | null;
|
|
65
84
|
};
|
|
66
85
|
|
|
@@ -207,16 +226,12 @@ export function cacheByRangeResponses({
|
|
|
207
226
|
if (downloadedPayloadEnvelopes !== null) {
|
|
208
227
|
payloadEnvelopes ??= new Map();
|
|
209
228
|
for (const [slot, envelope] of downloadedPayloadEnvelopes) {
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
// No block to pair this envelope with; drop silently
|
|
213
|
-
continue;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const payloadInput = seenPayloadEnvelopeInputCache.get(blockInput.blockRootHex);
|
|
229
|
+
const envelopeBlockRootHex = toRootHex(envelope.message.beaconBlockRoot);
|
|
230
|
+
const payloadInput = seenPayloadEnvelopeInputCache.get(envelopeBlockRootHex);
|
|
217
231
|
if (payloadInput === undefined) {
|
|
218
232
|
// Unreachable given the loop above seeded an entry for every gloas block in the batch.
|
|
219
|
-
|
|
233
|
+
// for the parent block, it's populated at BeaconChain init
|
|
234
|
+
throw new Error(`Missing PayloadEnvelopeInput for block ${envelopeBlockRootHex}`);
|
|
220
235
|
}
|
|
221
236
|
|
|
222
237
|
if (!payloadInput.hasPayloadEnvelope()) {
|
|
@@ -306,6 +321,8 @@ export async function downloadByRange({
|
|
|
306
321
|
blobsRequest,
|
|
307
322
|
columnsRequest,
|
|
308
323
|
envelopesRequest,
|
|
324
|
+
parentPayloadRequest,
|
|
325
|
+
parentPayloadCommitments,
|
|
309
326
|
peerDasMetrics,
|
|
310
327
|
}: DownloadAndCacheByRangeProps): Promise<
|
|
311
328
|
WarnResult<
|
|
@@ -322,6 +339,7 @@ export async function downloadByRange({
|
|
|
322
339
|
blobsRequest,
|
|
323
340
|
columnsRequest,
|
|
324
341
|
envelopesRequest,
|
|
342
|
+
parentPayloadRequest,
|
|
325
343
|
});
|
|
326
344
|
} catch (err) {
|
|
327
345
|
throw new DownloadByRangeError({
|
|
@@ -338,6 +356,8 @@ export async function downloadByRange({
|
|
|
338
356
|
blobsRequest,
|
|
339
357
|
columnsRequest,
|
|
340
358
|
envelopesRequest,
|
|
359
|
+
parentPayloadRequest,
|
|
360
|
+
parentPayloadCommitments,
|
|
341
361
|
peerDasMetrics,
|
|
342
362
|
...response,
|
|
343
363
|
});
|
|
@@ -353,13 +373,14 @@ export async function requestByRange({
|
|
|
353
373
|
blobsRequest,
|
|
354
374
|
columnsRequest,
|
|
355
375
|
envelopesRequest,
|
|
376
|
+
parentPayloadRequest,
|
|
356
377
|
}: DownloadByRangeRequests & {
|
|
357
378
|
network: INetwork;
|
|
358
379
|
peerIdStr: PeerIdStr;
|
|
359
380
|
}): Promise<DownloadByRangeResponses> {
|
|
360
381
|
let blocks: undefined | SignedBeaconBlock[];
|
|
361
382
|
let blobSidecars: undefined | deneb.BlobSidecars;
|
|
362
|
-
|
|
383
|
+
const columnSidecars: DataColumnSidecar[] = [];
|
|
363
384
|
const payloadEnvelopes: gloas.SignedExecutionPayloadEnvelope[] = [];
|
|
364
385
|
|
|
365
386
|
const requests: Promise<unknown>[] = [];
|
|
@@ -368,17 +389,6 @@ export async function requestByRange({
|
|
|
368
389
|
requests.push(
|
|
369
390
|
network.sendBeaconBlocksByRange(peerIdStr, blocksRequest).then((blockResponse) => {
|
|
370
391
|
blocks = blockResponse;
|
|
371
|
-
const firstBlock = blockResponse.at(0);
|
|
372
|
-
if (firstBlock && isGloasBeaconBlock(firstBlock.message)) {
|
|
373
|
-
return network
|
|
374
|
-
.sendExecutionPayloadEnvelopesByRoot(peerIdStr, [
|
|
375
|
-
firstBlock.message.body.signedExecutionPayloadBid.message.parentBlockRoot,
|
|
376
|
-
])
|
|
377
|
-
.then((envelopeResponse) => {
|
|
378
|
-
payloadEnvelopes?.unshift(...envelopeResponse);
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
return undefined;
|
|
382
392
|
})
|
|
383
393
|
);
|
|
384
394
|
}
|
|
@@ -394,7 +404,7 @@ export async function requestByRange({
|
|
|
394
404
|
if (columnsRequest) {
|
|
395
405
|
requests.push(
|
|
396
406
|
network.sendDataColumnSidecarsByRange(peerIdStr, columnsRequest).then((columnResponse) => {
|
|
397
|
-
columnSidecars
|
|
407
|
+
columnSidecars.push(...columnResponse);
|
|
398
408
|
})
|
|
399
409
|
);
|
|
400
410
|
}
|
|
@@ -402,11 +412,34 @@ export async function requestByRange({
|
|
|
402
412
|
if (envelopesRequest) {
|
|
403
413
|
requests.push(
|
|
404
414
|
network.sendExecutionPayloadEnvelopesByRange(peerIdStr, envelopesRequest).then((envelopeResponse) => {
|
|
405
|
-
payloadEnvelopes
|
|
415
|
+
payloadEnvelopes.push(...envelopeResponse);
|
|
406
416
|
})
|
|
407
417
|
);
|
|
408
418
|
}
|
|
409
419
|
|
|
420
|
+
// Only happens on the 1st batch of a SyncChain — fetches whichever pieces of the
|
|
421
|
+
// dangling-parent's payload are still missing from the seen-cache entry.
|
|
422
|
+
if (parentPayloadRequest?.envelopeBlockRoot) {
|
|
423
|
+
requests.push(
|
|
424
|
+
network
|
|
425
|
+
.sendExecutionPayloadEnvelopesByRoot(peerIdStr, [parentPayloadRequest.envelopeBlockRoot])
|
|
426
|
+
.then((envelopeResponse) => {
|
|
427
|
+
payloadEnvelopes.push(...envelopeResponse);
|
|
428
|
+
})
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
if (parentPayloadRequest?.blockRoot && parentPayloadRequest.columns && parentPayloadRequest.columns.length > 0) {
|
|
432
|
+
requests.push(
|
|
433
|
+
network
|
|
434
|
+
.sendDataColumnSidecarsByRoot(peerIdStr, [
|
|
435
|
+
{blockRoot: parentPayloadRequest.blockRoot, columns: parentPayloadRequest.columns},
|
|
436
|
+
])
|
|
437
|
+
.then((columnResponse) => {
|
|
438
|
+
columnSidecars.push(...columnResponse);
|
|
439
|
+
})
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
410
443
|
await Promise.all(requests);
|
|
411
444
|
|
|
412
445
|
return {
|
|
@@ -427,6 +460,8 @@ export async function validateResponses({
|
|
|
427
460
|
blobsRequest,
|
|
428
461
|
columnsRequest,
|
|
429
462
|
envelopesRequest,
|
|
463
|
+
parentPayloadRequest,
|
|
464
|
+
parentPayloadCommitments,
|
|
430
465
|
blocks,
|
|
431
466
|
blobSidecars,
|
|
432
467
|
columnSidecars,
|
|
@@ -436,6 +471,7 @@ export async function validateResponses({
|
|
|
436
471
|
DownloadByRangeResponses & {
|
|
437
472
|
config: ChainForkConfig;
|
|
438
473
|
batchBlocks?: IBlockInput[];
|
|
474
|
+
parentPayloadCommitments?: ParentPayloadCommitments;
|
|
439
475
|
peerDasMetrics?: BeaconMetrics["peerDas"] | null;
|
|
440
476
|
}): Promise<
|
|
441
477
|
WarnResult<
|
|
@@ -456,6 +492,11 @@ export async function validateResponses({
|
|
|
456
492
|
);
|
|
457
493
|
}
|
|
458
494
|
|
|
495
|
+
// `parentPayloadRequest` and `parentPayloadCommitments` must be supplied together
|
|
496
|
+
if ((parentPayloadRequest === undefined) !== (parentPayloadCommitments === undefined)) {
|
|
497
|
+
throw new Error("Coding error: parentPayloadRequest and parentPayloadCommitments must be both set or both unset");
|
|
498
|
+
}
|
|
499
|
+
|
|
459
500
|
const validatedResponses: ValidatedResponses = {};
|
|
460
501
|
let warnings: DownloadByRangeError[] | null = null;
|
|
461
502
|
|
|
@@ -467,21 +508,30 @@ export async function validateResponses({
|
|
|
467
508
|
validatedResponses.validatedBlocks = result.result;
|
|
468
509
|
}
|
|
469
510
|
|
|
511
|
+
const needsEnvelopeValidation = !!envelopesRequest || parentPayloadCommitments !== undefined;
|
|
470
512
|
const dataRequest = blobsRequest ?? columnsRequest;
|
|
471
|
-
if (!dataRequest && !
|
|
513
|
+
if (!dataRequest && !needsEnvelopeValidation) {
|
|
472
514
|
return {result: {responses: validatedResponses, payloadEnvelopes: null}, warnings};
|
|
473
515
|
}
|
|
474
516
|
|
|
475
517
|
if (!dataRequest) {
|
|
476
|
-
// Only envelope validation needed
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
payloadEnvelopes ?? []
|
|
518
|
+
// Only envelope and/or parent-by-root validation needed
|
|
519
|
+
if (parentPayloadCommitments !== undefined) {
|
|
520
|
+
const parentValidated = await validateParentPayloadColumns(
|
|
521
|
+
parentPayloadCommitments,
|
|
522
|
+
columnSidecars ?? [],
|
|
523
|
+
peerDasMetrics
|
|
483
524
|
);
|
|
525
|
+
if (parentValidated) {
|
|
526
|
+
validatedResponses.validatedColumnSidecars = [parentValidated];
|
|
527
|
+
}
|
|
484
528
|
}
|
|
529
|
+
const validatedPayloadEnvelopes = validateEnvelopesByRangeResponse(
|
|
530
|
+
validatedResponses.validatedBlocks ?? [],
|
|
531
|
+
batchBlocks,
|
|
532
|
+
payloadEnvelopes ?? [],
|
|
533
|
+
parentPayloadCommitments
|
|
534
|
+
);
|
|
485
535
|
return {result: {responses: validatedResponses, payloadEnvelopes: validatedPayloadEnvelopes}, warnings};
|
|
486
536
|
}
|
|
487
537
|
|
|
@@ -540,19 +590,64 @@ export async function validateResponses({
|
|
|
540
590
|
warnings = validatedColumnSidecarsResult.warnings;
|
|
541
591
|
}
|
|
542
592
|
|
|
543
|
-
//
|
|
593
|
+
// Parent columns (by-root): KZG-validate against parent's bid commitments and append.
|
|
594
|
+
if (parentPayloadCommitments !== undefined) {
|
|
595
|
+
const parentValidated = await validateParentPayloadColumns(
|
|
596
|
+
parentPayloadCommitments,
|
|
597
|
+
columnSidecars ?? [],
|
|
598
|
+
peerDasMetrics
|
|
599
|
+
);
|
|
600
|
+
if (parentValidated) {
|
|
601
|
+
validatedResponses.validatedColumnSidecars = [
|
|
602
|
+
...(validatedResponses.validatedColumnSidecars ?? []),
|
|
603
|
+
parentValidated,
|
|
604
|
+
];
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
544
608
|
let validatedPayloadEnvelopes: Map<Slot, gloas.SignedExecutionPayloadEnvelope> | null = null;
|
|
545
|
-
if (
|
|
609
|
+
if (needsEnvelopeValidation) {
|
|
546
610
|
validatedPayloadEnvelopes = validateEnvelopesByRangeResponse(
|
|
547
611
|
validatedResponses.validatedBlocks ?? [],
|
|
548
612
|
batchBlocks,
|
|
549
|
-
payloadEnvelopes ?? []
|
|
613
|
+
payloadEnvelopes ?? [],
|
|
614
|
+
parentPayloadCommitments
|
|
550
615
|
);
|
|
551
616
|
}
|
|
552
617
|
|
|
553
618
|
return {result: {responses: validatedResponses, payloadEnvelopes: validatedPayloadEnvelopes}, warnings};
|
|
554
619
|
}
|
|
555
620
|
|
|
621
|
+
async function validateParentPayloadColumns(
|
|
622
|
+
parentPayloadCommitments: ParentPayloadCommitments,
|
|
623
|
+
columnSidecars: DataColumnSidecar[],
|
|
624
|
+
peerDasMetrics?: BeaconMetrics["peerDas"] | null
|
|
625
|
+
): Promise<ValidatedColumnSidecars | null> {
|
|
626
|
+
const parentColumns: gloas.DataColumnSidecar[] = [];
|
|
627
|
+
for (const cs of columnSidecars) {
|
|
628
|
+
if (isGloasDataColumnSidecar(cs) && byteArrayEquals(cs.beaconBlockRoot, parentPayloadCommitments.blockRoot)) {
|
|
629
|
+
parentColumns.push(cs);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
if (parentColumns.length === 0) {
|
|
634
|
+
return null;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
parentColumns.sort((a, b) => a.index - b.index);
|
|
638
|
+
const parentSlot = parentColumns[0].slot;
|
|
639
|
+
|
|
640
|
+
await validateGloasBlockDataColumnSidecars(
|
|
641
|
+
parentSlot,
|
|
642
|
+
parentPayloadCommitments.blockRoot,
|
|
643
|
+
parentPayloadCommitments.kzgCommitments,
|
|
644
|
+
parentColumns,
|
|
645
|
+
peerDasMetrics
|
|
646
|
+
);
|
|
647
|
+
|
|
648
|
+
return {blockRoot: parentPayloadCommitments.blockRoot, columnSidecars: parentColumns};
|
|
649
|
+
}
|
|
650
|
+
|
|
556
651
|
/**
|
|
557
652
|
* Should not be called directly. Only exported for unit testing purposes
|
|
558
653
|
*
|
|
@@ -1060,6 +1155,9 @@ export enum DownloadByRangeErrorCode {
|
|
|
1060
1155
|
|
|
1061
1156
|
/** Envelope beaconBlockRoot does not match the block's root */
|
|
1062
1157
|
INVALID_ENVELOPE_BEACON_BLOCK_ROOT = "DOWNLOAD_BY_RANGE_ERROR_INVALID_ENVELOPE_BEACON_BLOCK_ROOT",
|
|
1158
|
+
|
|
1159
|
+
/** Block segment + envelopes failed chain-segment linearity / FULL-chain checks */
|
|
1160
|
+
INVALID_CHAIN_SEGMENT = "DOWNLOAD_BY_RANGE_ERROR_INVALID_CHAIN_SEGMENT",
|
|
1063
1161
|
}
|
|
1064
1162
|
|
|
1065
1163
|
export type DownloadByRangeErrorType =
|
|
@@ -1157,20 +1255,29 @@ export type DownloadByRangeErrorType =
|
|
|
1157
1255
|
slot: Slot;
|
|
1158
1256
|
expected: string;
|
|
1159
1257
|
actual: string;
|
|
1258
|
+
}
|
|
1259
|
+
| {
|
|
1260
|
+
code: DownloadByRangeErrorCode.INVALID_CHAIN_SEGMENT;
|
|
1261
|
+
slot: Slot;
|
|
1262
|
+
reason: string;
|
|
1160
1263
|
};
|
|
1161
1264
|
|
|
1162
1265
|
export class DownloadByRangeError extends LodestarError<DownloadByRangeErrorType> {}
|
|
1163
1266
|
|
|
1164
1267
|
/**
|
|
1165
1268
|
* Validates SignedExecutionPayloadEnvelopes received for a range request.
|
|
1166
|
-
*
|
|
1167
|
-
*
|
|
1168
|
-
*
|
|
1269
|
+
*
|
|
1270
|
+
* Three categories of envelope slots:
|
|
1271
|
+
* - In-batch slot whose block we have: verify envelope.beaconBlockRoot matches.
|
|
1272
|
+
* - Dangling-parent envelope (only when `parentPayloadCommitments` is set, i.e. the first
|
|
1273
|
+
* batch of a `SyncChain`): keep if `envelope.beaconBlockRoot === parentPayloadCommitments.blockRoot`.
|
|
1274
|
+
* - Other "orphan" envelopes (e.g. unrelated slots): ignored.
|
|
1169
1275
|
*/
|
|
1170
1276
|
export function validateEnvelopesByRangeResponse(
|
|
1171
1277
|
validatedBlocks: ValidatedBlock[],
|
|
1172
1278
|
batchBlocks: IBlockInput[] | undefined,
|
|
1173
|
-
payloadEnvelopes: gloas.SignedExecutionPayloadEnvelope[]
|
|
1279
|
+
payloadEnvelopes: gloas.SignedExecutionPayloadEnvelope[],
|
|
1280
|
+
parentPayloadCommitments?: ParentPayloadCommitments
|
|
1174
1281
|
): Map<Slot, gloas.SignedExecutionPayloadEnvelope> {
|
|
1175
1282
|
// Build a map of slot -> blockRoot for all blocks in the batch
|
|
1176
1283
|
const batchBlockRoots = new Map<Slot, Uint8Array>();
|
|
@@ -1189,8 +1296,15 @@ export function validateEnvelopesByRangeResponse(
|
|
|
1189
1296
|
const slot = payloadEnvelope.message.payload.slotNumber;
|
|
1190
1297
|
const batchBlockRoot = batchBlockRoots.get(slot);
|
|
1191
1298
|
|
|
1192
|
-
// Envelopes for slots not in the batch are silently ignored (orphaned payloads or a parent payload)
|
|
1193
1299
|
if (batchBlockRoot === undefined) {
|
|
1300
|
+
// Keep the requested dangling-parent envelope only when its beaconBlockRoot matches
|
|
1301
|
+
// exactly. All other unrelated envelopes are dropped.
|
|
1302
|
+
if (
|
|
1303
|
+
parentPayloadCommitments !== undefined &&
|
|
1304
|
+
byteArrayEquals(payloadEnvelope.message.beaconBlockRoot, parentPayloadCommitments.blockRoot)
|
|
1305
|
+
) {
|
|
1306
|
+
payloadEnvelopeMap.set(slot, payloadEnvelope);
|
|
1307
|
+
}
|
|
1194
1308
|
continue;
|
|
1195
1309
|
}
|
|
1196
1310
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {RequestError, RequestErrorCode} from "@lodestar/reqresp";
|
|
2
|
+
import {RATE_LIMITED_PEER_BACKOFF_MS} from "../constants.js";
|
|
3
|
+
|
|
4
|
+
export function getRateLimitedUntilMs(e: unknown): number | null {
|
|
5
|
+
if (!(e instanceof RequestError)) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
switch (e.type.code) {
|
|
10
|
+
case RequestErrorCode.RESP_RATE_LIMITED:
|
|
11
|
+
case RequestErrorCode.REQUEST_SELF_RATE_LIMITED:
|
|
12
|
+
return e.type.rateLimitedUntilMs ?? Date.now() + RATE_LIMITED_PEER_BACKOFF_MS;
|
|
13
|
+
default:
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|