@lodestar/beacon-node 1.43.0-dev.66d2c102e3 → 1.43.0-dev.6f485b1b61
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/api/impl/beacon/blocks/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/blocks/index.js +13 -3
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +45 -2
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +0 -1
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +68 -2
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/blocks/blockInput/blockInput.d.ts +3 -0
- package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
- package/lib/chain/blocks/blockInput/blockInput.js +4 -1
- package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +16 -31
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +9 -3
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +37 -15
- 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 +35 -21
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +12 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +28 -2
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +17 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/types.d.ts +2 -1
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.js +8 -0
- package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
- package/lib/chain/blocks/verifyBlock.d.ts +2 -1
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +30 -12
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts +0 -4
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +5 -2
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts +2 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js +16 -7
- package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +10 -6
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -1
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js +8 -3
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +25 -8
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +0 -11
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js +0 -4
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/proposerPreferences.d.ts +8 -1
- package/lib/chain/errors/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/errors/proposerPreferences.js +1 -0
- package/lib/chain/errors/proposerPreferences.js.map +1 -1
- package/lib/chain/initState.d.ts.map +1 -1
- package/lib/chain/initState.js +6 -1
- package/lib/chain/initState.js.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.d.ts +3 -2
- package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.js +26 -4
- package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +16 -18
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +12 -3
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +34 -22
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +1 -4
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/regen/regen.d.ts.map +1 -1
- package/lib/chain/regen/regen.js +1 -4
- package/lib/chain/regen/regen.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +21 -11
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +70 -20
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/seenCache/seenProposerPreferences.d.ts +8 -7
- package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +1 -1
- package/lib/chain/seenCache/seenProposerPreferences.js +11 -10
- package/lib/chain/seenCache/seenProposerPreferences.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +11 -8
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/proposerPreferences.d.ts.map +1 -1
- package/lib/chain/validation/proposerPreferences.js +39 -17
- package/lib/chain/validation/proposerPreferences.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +2 -0
- 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 +5 -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 +28 -10
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/index.js +5 -5
- package/lib/network/processor/index.js.map +1 -1
- package/lib/node/nodejs.js +2 -2
- package/lib/node/nodejs.js.map +1 -1
- package/lib/node/notifier.js +1 -7
- package/lib/node/notifier.js.map +1 -1
- package/lib/sync/constants.d.ts +3 -1
- package/lib/sync/constants.d.ts.map +1 -1
- package/lib/sync/constants.js +3 -4
- package/lib/sync/constants.js.map +1 -1
- package/lib/sync/range/batch.d.ts +23 -3
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +191 -36
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts +13 -2
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +61 -9
- 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 +14 -3
- package/lib/sync/range/range.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 -2
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +138 -57
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts +29 -8
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +104 -42
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRoot.js +10 -0
- package/lib/sync/utils/downloadByRoot.js.map +1 -1
- package/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +8 -6
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/blocks/index.ts +16 -3
- package/src/api/impl/beacon/pool/index.ts +83 -1
- package/src/api/impl/debug/index.ts +0 -1
- package/src/api/impl/validator/index.ts +82 -1
- package/src/chain/blocks/blockInput/blockInput.ts +4 -1
- package/src/chain/blocks/importBlock.ts +16 -50
- package/src/chain/blocks/importExecutionPayload.ts +51 -20
- package/src/chain/blocks/index.ts +32 -15
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +37 -3
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +18 -0
- package/src/chain/blocks/types.ts +2 -1
- package/src/chain/blocks/utils/chainSegment.ts +8 -0
- package/src/chain/blocks/verifyBlock.ts +45 -13
- package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +6 -4
- package/src/chain/blocks/verifyBlocksSanityChecks.ts +16 -6
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +14 -6
- package/src/chain/blocks/verifyPayloadsDataAvailability.ts +7 -4
- package/src/chain/chain.ts +29 -7
- package/src/chain/emitter.ts +0 -11
- package/src/chain/errors/proposerPreferences.ts +9 -1
- package/src/chain/initState.ts +9 -1
- package/src/chain/opPools/payloadAttestationPool.ts +29 -8
- package/src/chain/prepareNextSlot.ts +21 -29
- package/src/chain/produceBlock/produceBlockBody.ts +45 -27
- package/src/chain/regen/queued.ts +2 -7
- package/src/chain/regen/regen.ts +2 -7
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +90 -24
- package/src/chain/seenCache/seenProposerPreferences.ts +14 -11
- package/src/chain/validation/executionPayloadBid.ts +11 -8
- package/src/chain/validation/proposerPreferences.ts +37 -18
- package/src/network/interface.ts +1 -0
- package/src/network/network.ts +11 -0
- package/src/network/processor/gossipHandlers.ts +38 -11
- package/src/network/processor/index.ts +5 -5
- package/src/node/nodejs.ts +2 -2
- package/src/node/notifier.ts +1 -8
- package/src/sync/constants.ts +4 -4
- package/src/sync/range/batch.ts +240 -42
- package/src/sync/range/chain.ts +77 -10
- package/src/sync/range/range.ts +16 -3
- package/src/sync/sync.ts +13 -1
- package/src/sync/unknownBlock.ts +170 -60
- package/src/sync/utils/downloadByRange.ts +166 -44
- package/src/sync/utils/downloadByRoot.ts +12 -0
- package/src/util/sszBytes.ts +8 -6
|
@@ -8,7 +8,9 @@ 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,
|
|
@@ -45,6 +47,22 @@ export type DownloadByRangeRequests = {
|
|
|
45
47
|
blobsRequest?: deneb.BlobSidecarsByRangeRequest;
|
|
46
48
|
columnsRequest?: fulu.DataColumnSidecarsByRangeRequest;
|
|
47
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;
|
|
48
66
|
};
|
|
49
67
|
|
|
50
68
|
export type DownloadByRangeResponses = {
|
|
@@ -60,6 +78,8 @@ export type DownloadAndCacheByRangeProps = DownloadByRangeRequests & {
|
|
|
60
78
|
logger: Logger;
|
|
61
79
|
peerIdStr: string;
|
|
62
80
|
batchBlocks?: IBlockInput[];
|
|
81
|
+
/** Required when `parentPayloadRequest` is set; supplies the data needed to validate the parent's columns. */
|
|
82
|
+
parentPayloadCommitments?: ParentPayloadCommitments;
|
|
63
83
|
peerDasMetrics?: BeaconMetrics["peerDas"] | null;
|
|
64
84
|
};
|
|
65
85
|
|
|
@@ -185,33 +205,33 @@ export function cacheByRangeResponses({
|
|
|
185
205
|
}
|
|
186
206
|
}
|
|
187
207
|
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
208
|
+
// Seed seenPayloadEnvelopeInputCache for every gloas block in the batch, regardless of whether
|
|
209
|
+
// the peer returned its envelope. Without this, a block returned without its envelope would be
|
|
210
|
+
// imported with no cache entry, and later payload-by-root sync would throw
|
|
211
|
+
// "Missing PayloadEnvelopeInput for known block" (see issue #9306).
|
|
212
|
+
for (const blockInput of updatedBatchBlocks.values()) {
|
|
213
|
+
if (!blockInput.hasBlock() || !isForkPostGloas(blockInput.forkName)) continue;
|
|
214
|
+
seenPayloadEnvelopeInputCache.add({
|
|
215
|
+
blockRootHex: blockInput.blockRootHex,
|
|
216
|
+
block: blockInput.getBlock() as SignedBeaconBlock<ForkPostGloas>,
|
|
217
|
+
forkName: blockInput.forkName,
|
|
218
|
+
sampledColumns: custodyConfig.sampledColumns,
|
|
219
|
+
custodyColumns: custodyConfig.custodyColumns,
|
|
220
|
+
timeCreatedSec: seenTimestampSec,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
194
223
|
|
|
224
|
+
let payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null =
|
|
225
|
+
existingPayloadEnvelopes !== null ? new Map(existingPayloadEnvelopes) : null;
|
|
226
|
+
if (downloadedPayloadEnvelopes !== null) {
|
|
227
|
+
payloadEnvelopes ??= new Map();
|
|
195
228
|
for (const [slot, envelope] of downloadedPayloadEnvelopes) {
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
// No block to pair this envelope with; drop silently
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
const {blockRootHex} = blockInput;
|
|
202
|
-
|
|
203
|
-
// Reuse any existing PayloadEnvelopeInput (e.g. gossip arrived first) to avoid
|
|
204
|
-
// duplicate cache entries. If missing, create a fresh one from the block's bid.
|
|
205
|
-
let payloadInput = seenPayloadEnvelopeInputCache.get(blockRootHex);
|
|
229
|
+
const envelopeBlockRootHex = toRootHex(envelope.message.beaconBlockRoot);
|
|
230
|
+
const payloadInput = seenPayloadEnvelopeInputCache.get(envelopeBlockRootHex);
|
|
206
231
|
if (payloadInput === undefined) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
forkName: blockInput.forkName,
|
|
211
|
-
sampledColumns: custodyConfig.sampledColumns,
|
|
212
|
-
custodyColumns: custodyConfig.custodyColumns,
|
|
213
|
-
timeCreatedSec: seenTimestampSec,
|
|
214
|
-
});
|
|
232
|
+
// Unreachable given the loop above seeded an entry for every gloas block in the batch.
|
|
233
|
+
// for the parent block, it's populated at BeaconChain init
|
|
234
|
+
throw new Error(`Missing PayloadEnvelopeInput for block ${envelopeBlockRootHex}`);
|
|
215
235
|
}
|
|
216
236
|
|
|
217
237
|
if (!payloadInput.hasPayloadEnvelope()) {
|
|
@@ -301,6 +321,8 @@ export async function downloadByRange({
|
|
|
301
321
|
blobsRequest,
|
|
302
322
|
columnsRequest,
|
|
303
323
|
envelopesRequest,
|
|
324
|
+
parentPayloadRequest,
|
|
325
|
+
parentPayloadCommitments,
|
|
304
326
|
peerDasMetrics,
|
|
305
327
|
}: DownloadAndCacheByRangeProps): Promise<
|
|
306
328
|
WarnResult<
|
|
@@ -317,6 +339,7 @@ export async function downloadByRange({
|
|
|
317
339
|
blobsRequest,
|
|
318
340
|
columnsRequest,
|
|
319
341
|
envelopesRequest,
|
|
342
|
+
parentPayloadRequest,
|
|
320
343
|
});
|
|
321
344
|
} catch (err) {
|
|
322
345
|
throw new DownloadByRangeError({
|
|
@@ -333,6 +356,8 @@ export async function downloadByRange({
|
|
|
333
356
|
blobsRequest,
|
|
334
357
|
columnsRequest,
|
|
335
358
|
envelopesRequest,
|
|
359
|
+
parentPayloadRequest,
|
|
360
|
+
parentPayloadCommitments,
|
|
336
361
|
peerDasMetrics,
|
|
337
362
|
...response,
|
|
338
363
|
});
|
|
@@ -348,14 +373,15 @@ export async function requestByRange({
|
|
|
348
373
|
blobsRequest,
|
|
349
374
|
columnsRequest,
|
|
350
375
|
envelopesRequest,
|
|
376
|
+
parentPayloadRequest,
|
|
351
377
|
}: DownloadByRangeRequests & {
|
|
352
378
|
network: INetwork;
|
|
353
379
|
peerIdStr: PeerIdStr;
|
|
354
380
|
}): Promise<DownloadByRangeResponses> {
|
|
355
381
|
let blocks: undefined | SignedBeaconBlock[];
|
|
356
382
|
let blobSidecars: undefined | deneb.BlobSidecars;
|
|
357
|
-
|
|
358
|
-
|
|
383
|
+
const columnSidecars: DataColumnSidecar[] = [];
|
|
384
|
+
const payloadEnvelopes: gloas.SignedExecutionPayloadEnvelope[] = [];
|
|
359
385
|
|
|
360
386
|
const requests: Promise<unknown>[] = [];
|
|
361
387
|
|
|
@@ -378,7 +404,7 @@ export async function requestByRange({
|
|
|
378
404
|
if (columnsRequest) {
|
|
379
405
|
requests.push(
|
|
380
406
|
network.sendDataColumnSidecarsByRange(peerIdStr, columnsRequest).then((columnResponse) => {
|
|
381
|
-
columnSidecars
|
|
407
|
+
columnSidecars.push(...columnResponse);
|
|
382
408
|
})
|
|
383
409
|
);
|
|
384
410
|
}
|
|
@@ -386,11 +412,34 @@ export async function requestByRange({
|
|
|
386
412
|
if (envelopesRequest) {
|
|
387
413
|
requests.push(
|
|
388
414
|
network.sendExecutionPayloadEnvelopesByRange(peerIdStr, envelopesRequest).then((envelopeResponse) => {
|
|
389
|
-
payloadEnvelopes
|
|
415
|
+
payloadEnvelopes.push(...envelopeResponse);
|
|
390
416
|
})
|
|
391
417
|
);
|
|
392
418
|
}
|
|
393
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
|
+
|
|
394
443
|
await Promise.all(requests);
|
|
395
444
|
|
|
396
445
|
return {
|
|
@@ -411,6 +460,8 @@ export async function validateResponses({
|
|
|
411
460
|
blobsRequest,
|
|
412
461
|
columnsRequest,
|
|
413
462
|
envelopesRequest,
|
|
463
|
+
parentPayloadRequest,
|
|
464
|
+
parentPayloadCommitments,
|
|
414
465
|
blocks,
|
|
415
466
|
blobSidecars,
|
|
416
467
|
columnSidecars,
|
|
@@ -420,6 +471,7 @@ export async function validateResponses({
|
|
|
420
471
|
DownloadByRangeResponses & {
|
|
421
472
|
config: ChainForkConfig;
|
|
422
473
|
batchBlocks?: IBlockInput[];
|
|
474
|
+
parentPayloadCommitments?: ParentPayloadCommitments;
|
|
423
475
|
peerDasMetrics?: BeaconMetrics["peerDas"] | null;
|
|
424
476
|
}): Promise<
|
|
425
477
|
WarnResult<
|
|
@@ -440,6 +492,11 @@ export async function validateResponses({
|
|
|
440
492
|
);
|
|
441
493
|
}
|
|
442
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
|
+
|
|
443
500
|
const validatedResponses: ValidatedResponses = {};
|
|
444
501
|
let warnings: DownloadByRangeError[] | null = null;
|
|
445
502
|
|
|
@@ -451,21 +508,30 @@ export async function validateResponses({
|
|
|
451
508
|
validatedResponses.validatedBlocks = result.result;
|
|
452
509
|
}
|
|
453
510
|
|
|
511
|
+
const needsEnvelopeValidation = !!envelopesRequest || parentPayloadCommitments !== undefined;
|
|
454
512
|
const dataRequest = blobsRequest ?? columnsRequest;
|
|
455
|
-
if (!dataRequest && !
|
|
513
|
+
if (!dataRequest && !needsEnvelopeValidation) {
|
|
456
514
|
return {result: {responses: validatedResponses, payloadEnvelopes: null}, warnings};
|
|
457
515
|
}
|
|
458
516
|
|
|
459
517
|
if (!dataRequest) {
|
|
460
|
-
// Only envelope validation needed
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
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
|
|
467
524
|
);
|
|
525
|
+
if (parentValidated) {
|
|
526
|
+
validatedResponses.validatedColumnSidecars = [parentValidated];
|
|
527
|
+
}
|
|
468
528
|
}
|
|
529
|
+
const validatedPayloadEnvelopes = validateEnvelopesByRangeResponse(
|
|
530
|
+
validatedResponses.validatedBlocks ?? [],
|
|
531
|
+
batchBlocks,
|
|
532
|
+
payloadEnvelopes ?? [],
|
|
533
|
+
parentPayloadCommitments
|
|
534
|
+
);
|
|
469
535
|
return {result: {responses: validatedResponses, payloadEnvelopes: validatedPayloadEnvelopes}, warnings};
|
|
470
536
|
}
|
|
471
537
|
|
|
@@ -524,19 +590,64 @@ export async function validateResponses({
|
|
|
524
590
|
warnings = validatedColumnSidecarsResult.warnings;
|
|
525
591
|
}
|
|
526
592
|
|
|
527
|
-
//
|
|
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
|
+
|
|
528
608
|
let validatedPayloadEnvelopes: Map<Slot, gloas.SignedExecutionPayloadEnvelope> | null = null;
|
|
529
|
-
if (
|
|
609
|
+
if (needsEnvelopeValidation) {
|
|
530
610
|
validatedPayloadEnvelopes = validateEnvelopesByRangeResponse(
|
|
531
611
|
validatedResponses.validatedBlocks ?? [],
|
|
532
612
|
batchBlocks,
|
|
533
|
-
payloadEnvelopes ?? []
|
|
613
|
+
payloadEnvelopes ?? [],
|
|
614
|
+
parentPayloadCommitments
|
|
534
615
|
);
|
|
535
616
|
}
|
|
536
617
|
|
|
537
618
|
return {result: {responses: validatedResponses, payloadEnvelopes: validatedPayloadEnvelopes}, warnings};
|
|
538
619
|
}
|
|
539
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
|
+
|
|
540
651
|
/**
|
|
541
652
|
* Should not be called directly. Only exported for unit testing purposes
|
|
542
653
|
*
|
|
@@ -1147,14 +1258,18 @@ export class DownloadByRangeError extends LodestarError<DownloadByRangeErrorType
|
|
|
1147
1258
|
|
|
1148
1259
|
/**
|
|
1149
1260
|
* Validates SignedExecutionPayloadEnvelopes received for a range request.
|
|
1150
|
-
*
|
|
1151
|
-
*
|
|
1152
|
-
*
|
|
1261
|
+
*
|
|
1262
|
+
* Three categories of envelope slots:
|
|
1263
|
+
* - In-batch slot whose block we have: verify envelope.beaconBlockRoot matches.
|
|
1264
|
+
* - Dangling-parent envelope (only when `parentPayloadCommitments` is set, i.e. the first
|
|
1265
|
+
* batch of a `SyncChain`): keep if `envelope.beaconBlockRoot === parentPayloadCommitments.blockRoot`.
|
|
1266
|
+
* - Other "orphan" envelopes (e.g. unrelated slots): ignored.
|
|
1153
1267
|
*/
|
|
1154
1268
|
export function validateEnvelopesByRangeResponse(
|
|
1155
1269
|
validatedBlocks: ValidatedBlock[],
|
|
1156
1270
|
batchBlocks: IBlockInput[] | undefined,
|
|
1157
|
-
payloadEnvelopes: gloas.SignedExecutionPayloadEnvelope[]
|
|
1271
|
+
payloadEnvelopes: gloas.SignedExecutionPayloadEnvelope[],
|
|
1272
|
+
parentPayloadCommitments?: ParentPayloadCommitments
|
|
1158
1273
|
): Map<Slot, gloas.SignedExecutionPayloadEnvelope> {
|
|
1159
1274
|
// Build a map of slot -> blockRoot for all blocks in the batch
|
|
1160
1275
|
const batchBlockRoots = new Map<Slot, Uint8Array>();
|
|
@@ -1173,8 +1288,15 @@ export function validateEnvelopesByRangeResponse(
|
|
|
1173
1288
|
const slot = payloadEnvelope.message.payload.slotNumber;
|
|
1174
1289
|
const batchBlockRoot = batchBlockRoots.get(slot);
|
|
1175
1290
|
|
|
1176
|
-
// Envelopes for slots not in the batch are silently ignored (orphaned payloads)
|
|
1177
1291
|
if (batchBlockRoot === undefined) {
|
|
1292
|
+
// Keep the requested dangling-parent envelope only when its beaconBlockRoot matches
|
|
1293
|
+
// exactly. All other unrelated envelopes are dropped.
|
|
1294
|
+
if (
|
|
1295
|
+
parentPayloadCommitments !== undefined &&
|
|
1296
|
+
byteArrayEquals(payloadEnvelope.message.beaconBlockRoot, parentPayloadCommitments.blockRoot)
|
|
1297
|
+
) {
|
|
1298
|
+
payloadEnvelopeMap.set(slot, payloadEnvelope);
|
|
1299
|
+
}
|
|
1178
1300
|
continue;
|
|
1179
1301
|
}
|
|
1180
1302
|
|
|
@@ -3,6 +3,7 @@ import {ChainForkConfig} from "@lodestar/config";
|
|
|
3
3
|
import {
|
|
4
4
|
ForkPostDeneb,
|
|
5
5
|
ForkPostFulu,
|
|
6
|
+
ForkPostGloas,
|
|
6
7
|
ForkPreFulu,
|
|
7
8
|
isForkPostDeneb,
|
|
8
9
|
isForkPostFulu,
|
|
@@ -114,6 +115,17 @@ export async function downloadByRoot({
|
|
|
114
115
|
});
|
|
115
116
|
}
|
|
116
117
|
|
|
118
|
+
if (isForkPostGloas(blockInput.forkName)) {
|
|
119
|
+
chain.seenPayloadEnvelopeInputCache.add({
|
|
120
|
+
blockRootHex: rootHex,
|
|
121
|
+
block: blockInput.getBlock() as SignedBeaconBlock<ForkPostGloas>,
|
|
122
|
+
forkName: blockInput.forkName,
|
|
123
|
+
sampledColumns: chain.custodyConfig.sampledColumns,
|
|
124
|
+
custodyColumns: chain.custodyConfig.custodyColumns,
|
|
125
|
+
timeCreatedSec: Date.now() / 1000,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
117
129
|
const hasAllDataPreDownload = blockInput.hasBlockAndAllData();
|
|
118
130
|
|
|
119
131
|
if (isBlockInputBlobs(blockInput) && !hasAllDataPreDownload) {
|
package/src/util/sszBytes.ts
CHANGED
|
@@ -558,9 +558,10 @@ export function getBeaconBlockRootFromDataColumnSidecarSerialized(data: Uint8Arr
|
|
|
558
558
|
* └─ ExecutionPayloadEnvelope (starts at byte 100):
|
|
559
559
|
* ├─ 4 bytes: payload offset
|
|
560
560
|
* ├─ 4 bytes: executionRequests offset
|
|
561
|
-
* ├─ 8 bytes: builderIndex
|
|
562
|
-
* ├─ 32 bytes: beaconBlockRoot
|
|
563
|
-
*
|
|
561
|
+
* ├─ 8 bytes: builderIndex (offset 108-115)
|
|
562
|
+
* ├─ 32 bytes: beaconBlockRoot (offset 116-147)
|
|
563
|
+
* ├─ 32 bytes: parentBeaconBlockRoot (offset 148-179) — new in Gloas alpha.6 (consensus-specs#5152)
|
|
564
|
+
* └─ variable: payload data (starts at envelope + 80)
|
|
564
565
|
* └─ ExecutionPayload fixed portion includes slotNumber at offset 532
|
|
565
566
|
*/
|
|
566
567
|
const SIGNED_EXECUTION_PAYLOAD_ENVELOPE_MESSAGE_OFFSET = 4;
|
|
@@ -576,12 +577,13 @@ const BEACON_BLOCK_ROOT_OFFSET_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE =
|
|
|
576
577
|
EXECUTION_PAYLOAD_ENVELOPE_REQUESTS_OFFSET +
|
|
577
578
|
EXECUTION_PAYLOAD_ENVELOPE_BUILDER_INDEX_SIZE; // 116
|
|
578
579
|
|
|
579
|
-
// Envelope fixed portion
|
|
580
|
+
// Envelope fixed portion: payload_offset(4) + requests_offset(4) + builderIndex(8) + beaconBlockRoot(32) + parentBeaconBlockRoot(32) = 80
|
|
580
581
|
const EXECUTION_PAYLOAD_ENVELOPE_FIXED_SIZE =
|
|
581
582
|
EXECUTION_PAYLOAD_ENVELOPE_PAYLOAD_OFFSET +
|
|
582
583
|
EXECUTION_PAYLOAD_ENVELOPE_REQUESTS_OFFSET +
|
|
583
584
|
EXECUTION_PAYLOAD_ENVELOPE_BUILDER_INDEX_SIZE +
|
|
584
|
-
ROOT_SIZE
|
|
585
|
+
ROOT_SIZE +
|
|
586
|
+
ROOT_SIZE; // 80
|
|
585
587
|
|
|
586
588
|
// slotNumber offset within ExecutionPayload fixed portion:
|
|
587
589
|
// parentHash(32) + feeRecipient(20) + stateRoot(32) + receiptsRoot(32) + logsBloom(256) +
|
|
@@ -595,7 +597,7 @@ const ENVELOPE_START_IN_SIGNED =
|
|
|
595
597
|
SIGNED_EXECUTION_PAYLOAD_ENVELOPE_MESSAGE_OFFSET + SIGNED_EXECUTION_PAYLOAD_ENVELOPE_SIGNATURE_SIZE; // 100
|
|
596
598
|
|
|
597
599
|
const SLOT_OFFSET_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE =
|
|
598
|
-
ENVELOPE_START_IN_SIGNED + EXECUTION_PAYLOAD_ENVELOPE_FIXED_SIZE + SLOT_NUMBER_OFFSET_IN_EXECUTION_PAYLOAD; // 100 +
|
|
600
|
+
ENVELOPE_START_IN_SIGNED + EXECUTION_PAYLOAD_ENVELOPE_FIXED_SIZE + SLOT_NUMBER_OFFSET_IN_EXECUTION_PAYLOAD; // 100 + 80 + 532 = 712
|
|
599
601
|
|
|
600
602
|
export function getSlotFromExecutionPayloadEnvelopeSerialized(data: Uint8Array): Slot | null {
|
|
601
603
|
if (data.length < SLOT_OFFSET_IN_SIGNED_EXECUTION_PAYLOAD_ENVELOPE + SLOT_SIZE) {
|