@lodestar/beacon-node 1.43.0-dev.2fba242f5d → 1.43.0-dev.3bcc6d0ad5

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.
Files changed (56) hide show
  1. package/lib/api/impl/debug/index.d.ts.map +1 -1
  2. package/lib/api/impl/debug/index.js +1 -0
  3. package/lib/api/impl/debug/index.js.map +1 -1
  4. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  5. package/lib/chain/blocks/importBlock.js +2 -14
  6. package/lib/chain/blocks/importBlock.js.map +1 -1
  7. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  8. package/lib/chain/blocks/importExecutionPayload.js +3 -1
  9. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  10. package/lib/chain/blocks/index.d.ts.map +1 -1
  11. package/lib/chain/blocks/index.js +17 -30
  12. package/lib/chain/blocks/index.js.map +1 -1
  13. package/lib/chain/blocks/types.d.ts +1 -2
  14. package/lib/chain/blocks/types.d.ts.map +1 -1
  15. package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
  16. package/lib/chain/blocks/utils/chainSegment.js +0 -8
  17. package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
  18. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  19. package/lib/chain/blocks/verifyBlock.js +6 -5
  20. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  21. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts +4 -0
  22. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts.map +1 -1
  23. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +2 -5
  24. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
  25. package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts +1 -2
  26. package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
  27. package/lib/chain/blocks/verifyBlocksSanityChecks.js +7 -16
  28. package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
  29. package/lib/chain/regen/queued.d.ts.map +1 -1
  30. package/lib/chain/regen/queued.js +4 -1
  31. package/lib/chain/regen/queued.js.map +1 -1
  32. package/lib/chain/regen/regen.d.ts.map +1 -1
  33. package/lib/chain/regen/regen.js +4 -1
  34. package/lib/chain/regen/regen.js.map +1 -1
  35. package/lib/node/nodejs.js +2 -2
  36. package/lib/node/nodejs.js.map +1 -1
  37. package/lib/node/notifier.js +7 -1
  38. package/lib/node/notifier.js.map +1 -1
  39. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  40. package/lib/sync/utils/downloadByRange.js +4 -15
  41. package/lib/sync/utils/downloadByRange.js.map +1 -1
  42. package/package.json +15 -15
  43. package/src/api/impl/debug/index.ts +1 -0
  44. package/src/chain/blocks/importBlock.ts +3 -14
  45. package/src/chain/blocks/importExecutionPayload.ts +3 -1
  46. package/src/chain/blocks/index.ts +9 -20
  47. package/src/chain/blocks/types.ts +1 -2
  48. package/src/chain/blocks/utils/chainSegment.ts +0 -8
  49. package/src/chain/blocks/verifyBlock.ts +5 -7
  50. package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +4 -6
  51. package/src/chain/blocks/verifyBlocksSanityChecks.ts +6 -16
  52. package/src/chain/regen/queued.ts +7 -2
  53. package/src/chain/regen/regen.ts +7 -2
  54. package/src/node/nodejs.ts +2 -2
  55. package/src/node/notifier.ts +8 -1
  56. package/src/sync/utils/downloadByRange.ts +3 -15
@@ -69,8 +69,8 @@ export async function importBlock(
69
69
  fullyVerifiedBlock: FullyVerifiedBlock,
70
70
  opts: ImportBlockOpts
71
71
  ): Promise<void> {
72
- const {blockInput, postState, parentBlockSlot, dataAvailabilityStatus, indexedAttestations} = fullyVerifiedBlock;
73
- let {executionStatus} = fullyVerifiedBlock;
72
+ const {blockInput, postState, parentBlockSlot, executionStatus, dataAvailabilityStatus, indexedAttestations} =
73
+ fullyVerifiedBlock;
74
74
  const block = blockInput.getBlock();
75
75
  const source = blockInput.getBlockSource();
76
76
  const {slot: blockSlot} = block.message;
@@ -105,23 +105,12 @@ export async function importBlock(
105
105
 
106
106
  // Should compute checkpoint balances before forkchoice.onBlock
107
107
  this.checkpointBalancesCache.processState(blockRootHex, postState);
108
- if (fork >= ForkSeq.gloas) {
109
- const parentRootHex = toRootHex(block.message.parentRoot);
110
- const parentBlock = this.forkChoice.getBlockHexDefaultStatus(parentRootHex);
111
- if (parentBlock === null) {
112
- throw Error(`Parent block not found in forkChoice, parentRoot=${parentRootHex}`);
113
- }
114
- if (parentBlock.executionStatus === ExecutionStatus.Invalid) {
115
- throw Error(`Parent block has invalid execution status, parentRoot=${parentRootHex}`);
116
- }
117
- executionStatus = parentBlock.executionStatus;
118
- }
119
108
  const blockSummary = this.forkChoice.onBlock(
120
109
  block.message,
121
110
  postState,
122
111
  blockDelaySec,
123
112
  currentSlot,
124
- executionStatus,
113
+ fork >= ForkSeq.gloas ? ExecutionStatus.PayloadSeparated : executionStatus,
125
114
  dataAvailabilityStatus
126
115
  );
127
116
 
@@ -61,6 +61,7 @@ function toForkChoiceExecutionStatus(status: ExecutionPayloadStatus): PayloadExe
61
61
  switch (status) {
62
62
  case ExecutionPayloadStatus.VALID:
63
63
  return ExecutionStatus.Valid;
64
+ // TODO GLOAS: Handle optimistic import for payload
64
65
  case ExecutionPayloadStatus.SYNCING:
65
66
  case ExecutionPayloadStatus.ACCEPTED:
66
67
  return ExecutionStatus.Syncing;
@@ -254,7 +255,8 @@ export async function importExecutionPayload(
254
255
  builderIndex: envelope.builderIndex,
255
256
  blockHash: blockHashHex,
256
257
  blockRoot: blockRootHex,
257
- executionOptimistic: execStatus === ExecutionStatus.Syncing,
258
+ // TODO GLOAS: revisit once we support optimistic import
259
+ executionOptimistic: false,
258
260
  });
259
261
  }
260
262
 
@@ -65,7 +65,7 @@ export async function processBlocks(
65
65
  }
66
66
 
67
67
  try {
68
- const {relevantBlocks, parentSlots, parentBlock} = verifyBlocksSanityChecks(this, blocks, payloadEnvelopes, opts);
68
+ const {relevantBlocks, parentSlots, parentBlock} = verifyBlocksSanityChecks(this, blocks, opts);
69
69
 
70
70
  // No relevant blocks, skip verifyBlocksInEpoch()
71
71
  if (relevantBlocks.length === 0 || parentBlock === null) {
@@ -109,10 +109,8 @@ export async function processBlocks(
109
109
  }
110
110
 
111
111
  const {executionStatuses} = segmentExecStatus;
112
- const verifiedBlocksBySlot = new Map<Slot, FullyVerifiedBlock>();
113
- for (let i = 0; i < relevantBlocks.length; i++) {
114
- const block = relevantBlocks[i];
115
- verifiedBlocksBySlot.set(block.getBlock().message.slot, {
112
+ const fullyVerifiedBlocks = relevantBlocks.map(
113
+ (block, i): FullyVerifiedBlock => ({
116
114
  blockInput: block,
117
115
  postState: postStates[i],
118
116
  parentBlockSlot: parentSlots[i],
@@ -123,23 +121,14 @@ export async function processBlocks(
123
121
  indexedAttestations: indexedAttestationsByBlock[i],
124
122
  // TODO: Make this param mandatory and capture in gossip
125
123
  seenTimestampSec: opts.seenTimestampSec ?? Math.floor(Date.now() / 1000),
126
- });
127
- }
124
+ })
125
+ );
128
126
 
129
- // Iterate slots from the original `blocks` input (which spans the entire batch including
130
- // slots filtered out of `relevantBlocks`). The first batch of a checkpoint sync may contain
131
- // a payload at the anchor slot whose block is already in fork-choice (added by
132
- // initializeForkChoice as PENDING+EMPTY) and therefore not in verifiedBlocksBySlot — the
133
- // payload still needs to be imported here to populate the anchor's FULL variant so
134
- // subsequent slots can find their parent payload.
135
- const slots = Array.from(new Set(blocks.map((b) => b.getBlock().message.slot)));
136
- for (const slot of slots) {
137
- const fullyVerifiedBlock = verifiedBlocksBySlot.get(slot);
138
- if (fullyVerifiedBlock !== undefined) {
139
- // TODO: Consider batching importBlock too if it takes significant time
140
- await importBlock.call(this, fullyVerifiedBlock, opts);
141
- }
127
+ for (const fullyVerifiedBlock of fullyVerifiedBlocks) {
128
+ // TODO: Consider batching importBlock too if it takes significant time
129
+ await importBlock.call(this, fullyVerifiedBlock, opts);
142
130
 
131
+ const slot = fullyVerifiedBlock.blockInput.getBlock().message.slot;
143
132
  const payloadInput = payloadEnvelopes?.get(slot);
144
133
  if (payloadInput?.hasPayloadEnvelope()) {
145
134
  if (!payloadInput.isComplete()) {
@@ -94,8 +94,7 @@ export type ImportBlockOpts = {
94
94
  *
95
95
  * `executionStatus` reflects the outcome of execution payload verification at block-import time:
96
96
  * - pre-gloas: Valid | Syncing | PreMerge (from EL notifyNewPayload against the in-block payload)
97
- * - post-gloas: inherited from parent's chain (Valid/Syncing) by importBlock; payload arrives
98
- * separately as an envelope and creates the FULL variant later via onExecutionPayload
97
+ * - post-gloas: PayloadSeparated (payload arrives separately as an envelope and is imported later)
99
98
  */
100
99
  export type FullyVerifiedBlock = {
101
100
  blockInput: IBlockInput;
@@ -41,14 +41,6 @@ export function assertLinearChainSegment(
41
41
  // - EMPTY variant (no envelope for slot): execution hash is unchanged
42
42
  // null only for pre-merge parents, which cannot precede gloas blocks.
43
43
  let currentExecHash: string | null = parentBlock.executionPayloadBlockHash;
44
- // Checkpoint sync first batch: parent is the anchor PENDING whose executionPayloadBlockHash
45
- // is the inherited parentBlockHash semantic (= grandparent's payload), not its own payload.
46
- // If parent's own payload envelope arrives in this batch, advance currentExecHash to that
47
- // payload's blockHash so the segment validation sees the true EL chain head.
48
- const parentPayloadInput = payloadEnvelopes?.get(parentBlock.slot);
49
- if (parentPayloadInput?.hasPayloadEnvelope()) {
50
- currentExecHash = parentPayloadInput.getBlockHashHex();
51
- }
52
44
  // Track the execution hash before the last FULL advancement so we can recover
53
45
  // if the next block reveals that envelope was orphaned.
54
46
  let prevExecHash: string | null = currentExecHash;
@@ -125,17 +125,15 @@ export async function verifyBlocksInEpoch(
125
125
  }> =
126
126
  fork >= ForkSeq.gloas
127
127
  ? (async () => {
128
- // Validate DA for ALL payloads in the Map, not just those paired with blockInputs.
129
- // A checkpoint-sync batch may include a payload for a slot whose block was filtered
130
- // out of relevantBlocks (e.g., the anchor at the finalized slot); that payload still
131
- // needs DA validation so it can be imported in processBlocks.
132
- const payloadInputsForDa: PayloadEnvelopeInput[] =
133
- payloadEnvelopes !== null ? Array.from(payloadEnvelopes.values()) : [];
128
+ const payloadInputsForDa: PayloadEnvelopeInput[] = [];
129
+ for (const input of blockInputs) {
130
+ const pi = payloadEnvelopes?.get(input.slot);
131
+ if (pi !== undefined) payloadInputsForDa.push(pi);
132
+ }
134
133
  const {dataAvailabilityStatuses, availableTime} = await verifyPayloadsDataAvailability(
135
134
  payloadInputsForDa,
136
135
  abortController.signal
137
136
  );
138
-
139
137
  const payloadDAStatuses = new Map<Slot, DataAvailabilityStatus>();
140
138
  for (let i = 0; i < payloadInputsForDa.length; i++) {
141
139
  payloadDAStatuses.set(payloadInputsForDa[i].slot, dataAvailabilityStatuses[i]);
@@ -46,7 +46,8 @@ type VerifyBlockExecutionResponse =
46
46
  | VerifyExecutionErrorResponse
47
47
  | {executionStatus: ExecutionStatus.Valid; lvhResponse: LVHValidResponse; execError: null}
48
48
  | {executionStatus: ExecutionStatus.Syncing; lvhResponse?: LVHValidResponse; execError: null}
49
- | {executionStatus: ExecutionStatus.PreMerge; lvhResponse: undefined; execError: null};
49
+ | {executionStatus: ExecutionStatus.PreMerge; lvhResponse: undefined; execError: null}
50
+ | {executionStatus: ExecutionStatus.PayloadSeparated; lvhResponse: undefined; execError: null};
50
51
 
51
52
  /**
52
53
  * Verifies 1 or more execution payloads from a linear sequence of blocks.
@@ -144,10 +145,9 @@ export async function verifyBlockExecutionPayload(
144
145
  ): Promise<VerifyBlockExecutionResponse> {
145
146
  const block = blockInput.getBlock();
146
147
 
147
- // Gloas block doesn't have execution payload. Return Syncing as a placeholder; the actual
148
- // status for gloas PENDING/EMPTY is derived from parent's chain in importBlock.
148
+ // Gloas block doesn't have execution payload. Return right away
149
149
  if (isBlockInputNoData(blockInput)) {
150
- return {executionStatus: ExecutionStatus.Syncing, lvhResponse: undefined, execError: null};
150
+ return {executionStatus: ExecutionStatus.PayloadSeparated, lvhResponse: undefined, execError: null};
151
151
  }
152
152
 
153
153
  /** Not null if execution is enabled */
@@ -198,7 +198,6 @@ export async function verifyBlockExecutionPayload(
198
198
  executionStatus,
199
199
  latestValidExecHash: execResult.latestValidHash,
200
200
  invalidateFromParentBlockRoot: blockInput.parentRootHex,
201
- invalidateFromParentBlockHash: toRootHex(executionPayloadEnabled.parentHash),
202
201
  };
203
202
  const execError = new BlockError(block, {
204
203
  code: BlockErrorCode.EXECUTION_ENGINE_ERROR,
@@ -282,7 +281,6 @@ function getSegmentErrorResponse(
282
281
  executionStatus: ExecutionStatus.Invalid,
283
282
  latestValidExecHash: lvhResponse.latestValidExecHash,
284
283
  invalidateFromParentBlockRoot: parentBlock.blockRoot,
285
- invalidateFromParentBlockHash: parentBlock.executionPayloadBlockHash,
286
284
  };
287
285
  }
288
286
  }
@@ -7,7 +7,6 @@ import {IClock} from "../../util/clock.js";
7
7
  import {BlockError, BlockErrorCode} from "../errors/index.js";
8
8
  import {IChainOptions} from "../options.js";
9
9
  import {IBlockInput} from "./blockInput/types.js";
10
- import {PayloadEnvelopeInput} from "./payloadEnvelopeInput/payloadEnvelopeInput.js";
11
10
  import {ImportBlockOpts} from "./types.js";
12
11
 
13
12
  /**
@@ -31,7 +30,6 @@ export function verifyBlocksSanityChecks(
31
30
  blacklistedBlocks: Map<RootHex, Slot | null>;
32
31
  },
33
32
  blocks: IBlockInput[],
34
- payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
35
33
  opts: ImportBlockOpts
36
34
  ): {
37
35
  relevantBlocks: IBlockInput[];
@@ -102,21 +100,13 @@ export function verifyBlocksSanityChecks(
102
100
  const parentBlockHash = toRootHex(block.message.body.signedExecutionPayloadBid.message.parentBlockHash);
103
101
  const parentBlockWithPayload = chain.forkChoice.getBlockHexAndBlockHash(parentRoot, parentBlockHash);
104
102
  if (!parentBlockWithPayload) {
105
- // Checkpoint sync: parent's FULL variant may not be in fork-choice yet because the
106
- // anchor block is initialized with PENDING+EMPTY only. The parent's payload arrives
107
- // in the same batch via payloadEnvelopes and will be imported by processBlocks. If
108
- // a matching payload is in the Map, accept the parent as known.
109
- const parentPayloadInput = payloadEnvelopes?.get(parentBlockDefaultStatus.slot);
110
- if (parentPayloadInput?.getBlockHashHex() !== parentBlockHash) {
111
- throw new BlockError(block, {
112
- code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN,
113
- parentRoot,
114
- parentBlockHash,
115
- });
116
- }
117
- } else {
118
- parentBlock = parentBlockWithPayload;
103
+ throw new BlockError(block, {
104
+ code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN,
105
+ parentRoot,
106
+ parentBlockHash,
107
+ });
119
108
  }
109
+ parentBlock = parentBlockWithPayload;
120
110
  }
121
111
  // Parent is known to the fork-choice
122
112
  parentBlockSlot = parentBlock.slot;
@@ -1,7 +1,7 @@
1
1
  import {routes} from "@lodestar/api";
2
2
  import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
3
3
  import {IBeaconStateView, computeEpochAtSlot} from "@lodestar/state-transition";
4
- import {BeaconBlock, Epoch, RootHex, Slot, phase0} from "@lodestar/types";
4
+ import {BeaconBlock, Epoch, RootHex, Slot, isGloasBeaconBlock, phase0} from "@lodestar/types";
5
5
  import {Logger, toRootHex} from "@lodestar/utils";
6
6
  import {Metrics} from "../../metrics/index.js";
7
7
  import {JobItemQueue} from "../../util/queue/index.js";
@@ -88,7 +88,12 @@ export class QueuedStateRegenerator implements IStateRegenerator {
88
88
  */
89
89
  getPreStateSync(block: BeaconBlock): IBeaconStateView | null {
90
90
  const parentRoot = toRootHex(block.parentRoot);
91
- const parentBlock = this.forkChoice.getBlockHexDefaultStatus(parentRoot);
91
+ const parentBlock = isGloasBeaconBlock(block)
92
+ ? this.forkChoice.getBlockHexAndBlockHash(
93
+ parentRoot,
94
+ toRootHex(block.body.signedExecutionPayloadBid.message.parentBlockHash)
95
+ )
96
+ : this.forkChoice.getBlockHexDefaultStatus(parentRoot);
92
97
  if (!parentBlock) {
93
98
  throw new RegenError({
94
99
  code: RegenErrorCode.BLOCK_NOT_IN_FORKCHOICE,
@@ -9,7 +9,7 @@ import {
9
9
  computeEpochAtSlot,
10
10
  computeStartSlotAtEpoch,
11
11
  } from "@lodestar/state-transition";
12
- import {BeaconBlock, RootHex, SignedBeaconBlock, Slot} from "@lodestar/types";
12
+ import {BeaconBlock, RootHex, SignedBeaconBlock, Slot, isGloasBeaconBlock} from "@lodestar/types";
13
13
  import {Logger, fromHex, toRootHex} from "@lodestar/utils";
14
14
  import {IBeaconDb} from "../../db/index.js";
15
15
  import {Metrics} from "../../metrics/index.js";
@@ -57,7 +57,12 @@ export class StateRegenerator implements IStateRegeneratorInternal {
57
57
  regenCaller: RegenCaller
58
58
  ): Promise<IBeaconStateView> {
59
59
  const parentRoot = toRootHex(block.parentRoot);
60
- const parentBlock = this.modules.forkChoice.getBlockHexDefaultStatus(parentRoot);
60
+ const parentBlock = isGloasBeaconBlock(block)
61
+ ? this.modules.forkChoice.getBlockHexAndBlockHash(
62
+ parentRoot,
63
+ toRootHex(block.body.signedExecutionPayloadBid.message.parentBlockHash)
64
+ )
65
+ : this.modules.forkChoice.getBlockHexDefaultStatus(parentRoot);
61
66
  if (!parentBlock) {
62
67
  throw new RegenError({
63
68
  code: RegenErrorCode.BLOCK_NOT_IN_FORKCHOICE,
@@ -221,7 +221,7 @@ export class BeaconNode {
221
221
 
222
222
  let executionEngineOpts = opts.executionEngine;
223
223
  if (opts.executionEngine.mode === "mock") {
224
- const latestEth1BlockHash =
224
+ const eth1BlockHash =
225
225
  isStatePostBellatrix(anchorState) && anchorState.isExecutionStateType
226
226
  ? isStatePostGloas(anchorState)
227
227
  ? toRootHex(anchorState.latestBlockHash)
@@ -230,7 +230,7 @@ export class BeaconNode {
230
230
  executionEngineOpts = {
231
231
  ...opts.executionEngine,
232
232
  genesisBlockHash: ZERO_HASH_HEX,
233
- eth1BlockHash: opts.executionEngine.eth1BlockHash ?? latestEth1BlockHash,
233
+ eth1BlockHash,
234
234
  genesisTime: anchorState.genesisTime,
235
235
  config,
236
236
  };
@@ -167,7 +167,14 @@ function getHeadExecutionInfo(
167
167
  return [];
168
168
  }
169
169
 
170
- const executionStatusStr = headInfo.executionStatus.toLowerCase();
170
+ // A PayloadSeparated head is a gloas beacon block imported before its payload envelope
171
+ // arrives, in that case the exec-block row surfaces the inherited parent anchor (from the
172
+ // bid), which is already validated. Normalize to "valid" to avoid leaking internal
173
+ // fork-choice bookkeeping into the log. Once the payload envelope arrives and the FULL
174
+ // variant becomes head, executionStatus is Valid/Syncing naturally.
175
+ // TODO GLOAS: revisit once optimistic sync is implemented
176
+ const executionStatusStr =
177
+ headInfo.executionStatus === ExecutionStatus.PayloadSeparated ? "valid" : headInfo.executionStatus.toLowerCase();
171
178
 
172
179
  // Add execution status to notifier only if head is on/post bellatrix
173
180
  if (isStatePostBellatrix(headState) && headState.isExecutionStateType) {
@@ -14,7 +14,6 @@ import {
14
14
  deneb,
15
15
  fulu,
16
16
  gloas,
17
- isGloasBeaconBlock,
18
17
  isGloasDataColumnSidecar,
19
18
  phase0,
20
19
  } from "@lodestar/types";
@@ -362,7 +361,7 @@ export async function requestByRange({
362
361
  let blocks: undefined | SignedBeaconBlock[];
363
362
  let blobSidecars: undefined | deneb.BlobSidecars;
364
363
  let columnSidecars: undefined | DataColumnSidecar[];
365
- const payloadEnvelopes: gloas.SignedExecutionPayloadEnvelope[] = [];
364
+ let payloadEnvelopes: undefined | gloas.SignedExecutionPayloadEnvelope[];
366
365
 
367
366
  const requests: Promise<unknown>[] = [];
368
367
 
@@ -370,17 +369,6 @@ export async function requestByRange({
370
369
  requests.push(
371
370
  network.sendBeaconBlocksByRange(peerIdStr, blocksRequest).then((blockResponse) => {
372
371
  blocks = blockResponse;
373
- const firstBlock = blockResponse.at(0);
374
- if (firstBlock && isGloasBeaconBlock(firstBlock.message)) {
375
- return network
376
- .sendExecutionPayloadEnvelopesByRoot(peerIdStr, [
377
- firstBlock.message.body.signedExecutionPayloadBid.message.parentBlockRoot,
378
- ])
379
- .then((envelopeResponse) => {
380
- payloadEnvelopes?.unshift(...envelopeResponse);
381
- });
382
- }
383
- return undefined;
384
372
  })
385
373
  );
386
374
  }
@@ -404,7 +392,7 @@ export async function requestByRange({
404
392
  if (envelopesRequest) {
405
393
  requests.push(
406
394
  network.sendExecutionPayloadEnvelopesByRange(peerIdStr, envelopesRequest).then((envelopeResponse) => {
407
- payloadEnvelopes?.push(...envelopeResponse);
395
+ payloadEnvelopes = envelopeResponse;
408
396
  })
409
397
  );
410
398
  }
@@ -1191,7 +1179,7 @@ export function validateEnvelopesByRangeResponse(
1191
1179
  const slot = payloadEnvelope.message.payload.slotNumber;
1192
1180
  const batchBlockRoot = batchBlockRoots.get(slot);
1193
1181
 
1194
- // Envelopes for slots not in the batch are silently ignored (orphaned payloads or a parent payload)
1182
+ // Envelopes for slots not in the batch are silently ignored (orphaned payloads)
1195
1183
  if (batchBlockRoot === undefined) {
1196
1184
  continue;
1197
1185
  }