@lodestar/beacon-node 1.43.0-dev.3d6ae250a4 → 1.43.0-dev.4358217e12

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 (190) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +6 -5
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/lodestar/index.js +1 -1
  5. package/lib/api/impl/lodestar/index.js.map +1 -1
  6. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  7. package/lib/chain/blocks/importBlock.js +6 -3
  8. package/lib/chain/blocks/importBlock.js.map +1 -1
  9. package/lib/chain/blocks/importExecutionPayload.d.ts +18 -6
  10. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  11. package/lib/chain/blocks/importExecutionPayload.js +42 -18
  12. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  13. package/lib/chain/blocks/index.d.ts +5 -3
  14. package/lib/chain/blocks/index.d.ts.map +1 -1
  15. package/lib/chain/blocks/index.js +28 -9
  16. package/lib/chain/blocks/index.js.map +1 -1
  17. package/lib/chain/blocks/payloadEnvelopeProcessor.js +2 -2
  18. package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
  19. package/lib/chain/blocks/types.d.ts +2 -2
  20. package/lib/chain/blocks/types.d.ts.map +1 -1
  21. package/lib/chain/blocks/utils/chainSegment.d.ts +23 -2
  22. package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
  23. package/lib/chain/blocks/utils/chainSegment.js +81 -12
  24. package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
  25. package/lib/chain/blocks/verifyBlock.d.ts +3 -2
  26. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  27. package/lib/chain/blocks/verifyBlock.js +30 -5
  28. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  29. package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
  30. package/lib/chain/blocks/verifyBlocksSanityChecks.js +15 -4
  31. package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
  32. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
  33. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
  34. package/lib/chain/chain.d.ts +3 -2
  35. package/lib/chain/chain.d.ts.map +1 -1
  36. package/lib/chain/chain.js +10 -4
  37. package/lib/chain/chain.js.map +1 -1
  38. package/lib/chain/errors/blockError.d.ts +8 -1
  39. package/lib/chain/errors/blockError.d.ts.map +1 -1
  40. package/lib/chain/errors/blockError.js +2 -0
  41. package/lib/chain/errors/blockError.js.map +1 -1
  42. package/lib/chain/errors/index.d.ts +1 -0
  43. package/lib/chain/errors/index.d.ts.map +1 -1
  44. package/lib/chain/errors/index.js +1 -0
  45. package/lib/chain/errors/index.js.map +1 -1
  46. package/lib/chain/errors/proposerPreferences.d.ts +33 -0
  47. package/lib/chain/errors/proposerPreferences.d.ts.map +1 -0
  48. package/lib/chain/errors/proposerPreferences.js +13 -0
  49. package/lib/chain/errors/proposerPreferences.js.map +1 -0
  50. package/lib/chain/interface.d.ts +3 -2
  51. package/lib/chain/interface.d.ts.map +1 -1
  52. package/lib/chain/interface.js.map +1 -1
  53. package/lib/chain/opPools/payloadAttestationPool.d.ts +2 -2
  54. package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
  55. package/lib/chain/opPools/payloadAttestationPool.js +4 -4
  56. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  57. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  58. package/lib/chain/produceBlock/produceBlockBody.js +9 -4
  59. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  60. package/lib/chain/regen/interface.d.ts +1 -0
  61. package/lib/chain/regen/interface.d.ts.map +1 -1
  62. package/lib/chain/regen/interface.js +1 -0
  63. package/lib/chain/regen/interface.js.map +1 -1
  64. package/lib/chain/seenCache/index.d.ts +1 -0
  65. package/lib/chain/seenCache/index.d.ts.map +1 -1
  66. package/lib/chain/seenCache/index.js +1 -0
  67. package/lib/chain/seenCache/index.js.map +1 -1
  68. package/lib/chain/seenCache/seenProposerPreferences.d.ts +15 -0
  69. package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +1 -0
  70. package/lib/chain/seenCache/seenProposerPreferences.js +25 -0
  71. package/lib/chain/seenCache/seenProposerPreferences.js.map +1 -0
  72. package/lib/chain/validation/block.d.ts.map +1 -1
  73. package/lib/chain/validation/block.js +1 -0
  74. package/lib/chain/validation/block.js.map +1 -1
  75. package/lib/chain/validation/proposerPreferences.d.ts +8 -0
  76. package/lib/chain/validation/proposerPreferences.d.ts.map +1 -0
  77. package/lib/chain/validation/proposerPreferences.js +69 -0
  78. package/lib/chain/validation/proposerPreferences.js.map +1 -0
  79. package/lib/metrics/metrics/lodestar.d.ts +1 -0
  80. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  81. package/lib/metrics/metrics/lodestar.js +4 -0
  82. package/lib/metrics/metrics/lodestar.js.map +1 -1
  83. package/lib/network/gossip/interface.d.ts +7 -1
  84. package/lib/network/gossip/interface.d.ts.map +1 -1
  85. package/lib/network/gossip/interface.js +1 -0
  86. package/lib/network/gossip/interface.js.map +1 -1
  87. package/lib/network/gossip/scoringParameters.d.ts.map +1 -1
  88. package/lib/network/gossip/scoringParameters.js +12 -1
  89. package/lib/network/gossip/scoringParameters.js.map +1 -1
  90. package/lib/network/gossip/topic.d.ts +27 -766
  91. package/lib/network/gossip/topic.d.ts.map +1 -1
  92. package/lib/network/gossip/topic.js +6 -0
  93. package/lib/network/gossip/topic.js.map +1 -1
  94. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  95. package/lib/network/processor/gossipHandlers.js +10 -6
  96. package/lib/network/processor/gossipHandlers.js.map +1 -1
  97. package/lib/network/processor/gossipQueues/index.d.ts.map +1 -1
  98. package/lib/network/processor/gossipQueues/index.js +5 -0
  99. package/lib/network/processor/gossipQueues/index.js.map +1 -1
  100. package/lib/network/processor/index.d.ts.map +1 -1
  101. package/lib/network/processor/index.js +1 -0
  102. package/lib/network/processor/index.js.map +1 -1
  103. package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
  104. package/lib/network/reqresp/handlers/beaconBlocksByRange.js +14 -6
  105. package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
  106. package/lib/network/reqresp/handlers/blobSidecarsByRange.d.ts.map +1 -1
  107. package/lib/network/reqresp/handlers/blobSidecarsByRange.js +11 -5
  108. package/lib/network/reqresp/handlers/blobSidecarsByRange.js.map +1 -1
  109. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
  110. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +17 -5
  111. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
  112. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
  113. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +7 -4
  114. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
  115. package/lib/node/notifier.js +7 -1
  116. package/lib/node/notifier.js.map +1 -1
  117. package/lib/sync/range/batch.d.ts +12 -2
  118. package/lib/sync/range/batch.d.ts.map +1 -1
  119. package/lib/sync/range/batch.js +56 -30
  120. package/lib/sync/range/batch.js.map +1 -1
  121. package/lib/sync/range/chain.d.ts +6 -2
  122. package/lib/sync/range/chain.d.ts.map +1 -1
  123. package/lib/sync/range/chain.js +4 -3
  124. package/lib/sync/range/chain.js.map +1 -1
  125. package/lib/sync/range/range.d.ts.map +1 -1
  126. package/lib/sync/range/range.js +17 -6
  127. package/lib/sync/range/range.js.map +1 -1
  128. package/lib/sync/types.d.ts +34 -0
  129. package/lib/sync/types.d.ts.map +1 -1
  130. package/lib/sync/types.js +34 -0
  131. package/lib/sync/types.js.map +1 -1
  132. package/lib/sync/unknownBlock.d.ts +24 -1
  133. package/lib/sync/unknownBlock.d.ts.map +1 -1
  134. package/lib/sync/unknownBlock.js +649 -53
  135. package/lib/sync/unknownBlock.js.map +1 -1
  136. package/lib/sync/utils/downloadByRange.d.ts +46 -10
  137. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  138. package/lib/sync/utils/downloadByRange.js +147 -24
  139. package/lib/sync/utils/downloadByRange.js.map +1 -1
  140. package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
  141. package/lib/sync/utils/downloadByRoot.js +6 -2
  142. package/lib/sync/utils/downloadByRoot.js.map +1 -1
  143. package/lib/sync/utils/pendingBlocksTree.d.ts +0 -1
  144. package/lib/sync/utils/pendingBlocksTree.d.ts.map +1 -1
  145. package/lib/sync/utils/pendingBlocksTree.js +0 -9
  146. package/lib/sync/utils/pendingBlocksTree.js.map +1 -1
  147. package/package.json +15 -15
  148. package/src/api/impl/beacon/blocks/index.ts +8 -5
  149. package/src/api/impl/lodestar/index.ts +1 -1
  150. package/src/chain/blocks/importBlock.ts +4 -2
  151. package/src/chain/blocks/importExecutionPayload.ts +48 -19
  152. package/src/chain/blocks/index.ts +44 -12
  153. package/src/chain/blocks/payloadEnvelopeProcessor.ts +2 -2
  154. package/src/chain/blocks/types.ts +2 -2
  155. package/src/chain/blocks/utils/chainSegment.ts +106 -17
  156. package/src/chain/blocks/verifyBlock.ts +35 -6
  157. package/src/chain/blocks/verifyBlocksSanityChecks.ts +16 -7
  158. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +2 -2
  159. package/src/chain/chain.ts +14 -3
  160. package/src/chain/errors/blockError.ts +4 -1
  161. package/src/chain/errors/index.ts +1 -0
  162. package/src/chain/errors/proposerPreferences.ts +39 -0
  163. package/src/chain/interface.ts +7 -1
  164. package/src/chain/opPools/payloadAttestationPool.ts +4 -8
  165. package/src/chain/produceBlock/produceBlockBody.ts +12 -4
  166. package/src/chain/regen/interface.ts +1 -0
  167. package/src/chain/seenCache/index.ts +1 -0
  168. package/src/chain/seenCache/seenProposerPreferences.ts +29 -0
  169. package/src/chain/validation/block.ts +1 -0
  170. package/src/chain/validation/proposerPreferences.ts +91 -0
  171. package/src/metrics/metrics/lodestar.ts +4 -0
  172. package/src/network/gossip/interface.ts +6 -0
  173. package/src/network/gossip/scoringParameters.ts +14 -1
  174. package/src/network/gossip/topic.ts +6 -0
  175. package/src/network/processor/gossipHandlers.ts +15 -6
  176. package/src/network/processor/gossipQueues/index.ts +5 -0
  177. package/src/network/processor/index.ts +1 -0
  178. package/src/network/reqresp/handlers/beaconBlocksByRange.ts +14 -6
  179. package/src/network/reqresp/handlers/blobSidecarsByRange.ts +11 -5
  180. package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +17 -5
  181. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +7 -4
  182. package/src/node/notifier.ts +8 -1
  183. package/src/sync/range/batch.ts +90 -35
  184. package/src/sync/range/chain.ts +13 -5
  185. package/src/sync/range/range.ts +18 -6
  186. package/src/sync/types.ts +72 -0
  187. package/src/sync/unknownBlock.ts +810 -57
  188. package/src/sync/utils/downloadByRange.ts +256 -39
  189. package/src/sync/utils/downloadByRoot.ts +12 -2
  190. package/src/sync/utils/pendingBlocksTree.ts +0 -15
@@ -118,7 +118,7 @@ export function getLodestarApi({
118
118
  return {
119
119
  // biome-ignore lint/complexity/useLiteralKeys: The `blockProcessor` is a protected attribute
120
120
  data: (chain as BeaconChain)["blockProcessor"].jobQueue.getItems().map((item) => {
121
- const [blockInputs, opts] = item.args;
121
+ const [blockInputs, _payloadEnvelopes, opts] = item.args;
122
122
  return {
123
123
  blockSlots: blockInputs.map((blockInput) => blockInput.slot),
124
124
  jobOpts: opts,
@@ -128,6 +128,7 @@ export async function importBlock(
128
128
  blockDelaySec,
129
129
  currentSlot,
130
130
  fork >= ForkSeq.gloas ? ExecutionStatus.PayloadSeparated : executionStatus,
131
+ // TODO GLOAS: this is not useful post-gloas, may need to remove it?
131
132
  dataAvailabilityStatus
132
133
  );
133
134
 
@@ -135,8 +136,9 @@ export async function importBlock(
135
136
  // Some block event handlers require state being in state cache so need to do this before emitting EventType.block
136
137
  this.regen.processState(blockRootHex, postState);
137
138
 
138
- // For Gloas blocks, create PayloadEnvelopeInput so it's available for later payload import
139
- if (fork >= ForkSeq.gloas) {
139
+ // For range sync, PayloadEnvelope is created before reaching this
140
+ // we also don't need to trigger getBlobs() in that case
141
+ if (fork >= ForkSeq.gloas && !opts.fromRangeSync) {
140
142
  const payloadInput = this.seenPayloadEnvelopeInputCache.add({
141
143
  blockRootHex,
142
144
  block: block as SignedBeaconBlock<ForkPostGloas>,
@@ -1,7 +1,8 @@
1
1
  import {routes} from "@lodestar/api";
2
- import {ExecutionStatus, PayloadExecutionStatus} from "@lodestar/fork-choice";
2
+ import {ExecutionStatus, PayloadExecutionStatus, getSafeExecutionBlockHash} from "@lodestar/fork-choice";
3
3
  import {isStatePostGloas} from "@lodestar/state-transition";
4
- import {fromHex} from "@lodestar/utils";
4
+ import {fromHex, isErrorAborted} from "@lodestar/utils";
5
+ import {ZERO_HASH_HEX} from "../../constants/index.js";
5
6
  import {ExecutionPayloadStatus} from "../../execution/index.js";
6
7
  import {isQueueErrorAborted} from "../../util/queue/index.js";
7
8
  import {BeaconChain} from "../chain.js";
@@ -75,21 +76,23 @@ function toForkChoiceExecutionStatus(status: ExecutionPayloadStatus): PayloadExe
75
76
  * The envelope is only verified here, no state mutation. State effects from the payload
76
77
  * are applied on the next block via processParentExecutionPayload.
77
78
  *
79
+ * The DA wait must have run upstream (range sync awaits DA in `verifyBlocksInEpoch` for the
80
+ * whole segment; gossip / API path uses the `processExecutionPayload` wrapper below).
81
+ *
78
82
  * Steps:
79
83
  * 1. Emit `execution_payload_available` event for payload attestation
80
84
  * 2. Get the ProtoBlock from fork choice
81
- * 3. Wait for data columns to be available
82
- * 4. Regenerate state for envelope verification
83
- * 5. Verify envelope (fields against state, signature, and EL in parallel where possible)
84
- * 6. Persist verified payload envelope to hot DB (waits for write-queue space for backpressure)
85
- * 7. Update fork choice (transitions the block's PENDING variant to FULL)
85
+ * 3. Regenerate state for envelope verification
86
+ * 4. Verify envelope (fields against state, signature, and EL in parallel where possible)
87
+ * 5. Persist verified payload envelope to hot DB (waits for write-queue space for backpressure)
88
+ * 6. Update fork choice (transitions the block's PENDING variant to FULL)
89
+ * 7. Queue notifyForkchoiceUpdate to engine api
86
90
  * 8. Record metrics for payload envelope and column sources
87
91
  * 9. Emit `execution_payload` event
88
92
  */
89
93
  export async function importExecutionPayload(
90
94
  this: BeaconChain,
91
95
  payloadInput: PayloadEnvelopeInput,
92
- signal: AbortSignal,
93
96
  opts: ImportPayloadOpts = {}
94
97
  ): Promise<void> {
95
98
  const signedEnvelope = payloadInput.getPayloadEnvelope();
@@ -119,11 +122,7 @@ export async function importExecutionPayload(
119
122
  });
120
123
  }
121
124
 
122
- // 3. Wait for data columns to be available.
123
- // The helper is shared with future gloas sync services; take the single-item batch form here.
124
- await verifyPayloadsDataAvailability([payloadInput], signal);
125
-
126
- // 4. Regenerate state for envelope verification
125
+ // 3. Regenerate state for envelope verification
127
126
  const blockState = await this.regen.getBlockSlotState(
128
127
  protoBlock,
129
128
  protoBlock.slot,
@@ -137,7 +136,7 @@ export async function importExecutionPayload(
137
136
  });
138
137
  }
139
138
 
140
- // 5. Verify envelope fields against state first to fail fast before the EL + BLS work.
139
+ // 4. Verify envelope fields against state first to fail fast before the EL + BLS work.
141
140
  // When validSignature is true, gossip/API has already verified both the signature and the
142
141
  // executionRequestsRoot, so we skip those checks here.
143
142
  try {
@@ -154,7 +153,7 @@ export async function importExecutionPayload(
154
153
  );
155
154
  }
156
155
 
157
- // 5a. Run EL and signature verification in parallel
156
+ // 4a. Run EL and signature verification in parallel
158
157
  const [execResult, signatureValid] = await Promise.all([
159
158
  this.executionEngine.notifyNewPayload(
160
159
  fork,
@@ -176,12 +175,12 @@ export async function importExecutionPayload(
176
175
  ),
177
176
  ]);
178
177
 
179
- // 5b. Check signature verification result
178
+ // 4b. Check signature verification result
180
179
  if (!signatureValid) {
181
180
  throw new PayloadError({code: PayloadErrorCode.INVALID_SIGNATURE});
182
181
  }
183
182
 
184
- // 5c. Handle EL response
183
+ // 4c. Handle EL response
185
184
  switch (execResult.status) {
186
185
  case ExecutionPayloadStatus.VALID:
187
186
  break;
@@ -207,7 +206,7 @@ export async function importExecutionPayload(
207
206
  });
208
207
  }
209
208
 
210
- // 6. Persist payload envelope to hot DB. Wait for write-queue space here to apply backpressure
209
+ // 5. Persist payload envelope to hot DB. Wait for write-queue space here to apply backpressure
211
210
  // on the import pipeline during sync, then perform the write asynchronously to avoid blocking.
212
211
  await this.unfinalizedPayloadEnvelopeWrites.waitForSpace();
213
212
  this.unfinalizedPayloadEnvelopeWrites.push(payloadInput).catch((e) => {
@@ -220,10 +219,22 @@ export async function importExecutionPayload(
220
219
  }
221
220
  });
222
221
 
223
- // 7. Update fork choice, transitions the block's PENDING variant to FULL
222
+ // 6. Update fork choice, transitions the block's PENDING variant to FULL
224
223
  const execStatus = toForkChoiceExecutionStatus(execResult.status);
225
224
  this.forkChoice.onExecutionPayload(blockRootHex, blockHashHex, envelope.payload.blockNumber, execStatus);
226
225
 
226
+ // 7. Queue notifyForkchoiceUpdate to engine api
227
+ const head = this.forkChoice.getHead();
228
+ if (!this.opts.disableImportExecutionFcU && blockRootHex === head.blockRoot) {
229
+ const safeBlockHash = getSafeExecutionBlockHash(this.forkChoice);
230
+ const finalizedBlockHash = this.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
231
+ this.executionEngine.notifyForkchoiceUpdate(fork, blockHashHex, safeBlockHash, finalizedBlockHash).catch((e) => {
232
+ if (!isErrorAborted(e) && !isQueueErrorAborted(e)) {
233
+ this.logger.error("Error pushing notifyForkchoiceUpdate()", {blockHashHex, finalizedBlockHash}, e);
234
+ }
235
+ });
236
+ }
237
+
227
238
  // 8. Record metrics for payload envelope and column sources
228
239
  this.metrics?.importPayload.bySource.inc({source: payloadInput.getPayloadEnvelopeSource().source});
229
240
  for (const {source} of payloadInput.getSampledColumnsWithSource()) {
@@ -249,3 +260,21 @@ export async function importExecutionPayload(
249
260
  blockHash: blockHashHex,
250
261
  });
251
262
  }
263
+
264
+ /**
265
+ * Process an execution payload envelope end-to-end: wait for DA, then import.
266
+ *
267
+ * Used by the PayloadEnvelopeProcessor queue (gossip / API / unknown-payload sync) — i.e.
268
+ * callers that have NOT already awaited DA themselves. Range sync's inline dispatch in
269
+ * processBlocks skips this wrapper and calls `importExecutionPayload` directly, since
270
+ * `verifyBlocksInEpoch` already awaited DA for the segment.
271
+ */
272
+ export async function processExecutionPayload(
273
+ this: BeaconChain,
274
+ payloadInput: PayloadEnvelopeInput,
275
+ signal: AbortSignal,
276
+ opts: ImportPayloadOpts = {}
277
+ ): Promise<void> {
278
+ await verifyPayloadsDataAvailability([payloadInput], signal);
279
+ await importExecutionPayload.call(this, payloadInput, opts);
280
+ }
@@ -1,4 +1,4 @@
1
- import {SignedBeaconBlock} from "@lodestar/types";
1
+ import {SignedBeaconBlock, Slot} from "@lodestar/types";
2
2
  import {isErrorAborted, toRootHex} from "@lodestar/utils";
3
3
  import {Metrics} from "../../metrics/metrics.js";
4
4
  import {nextEventLoop} from "../../util/eventLoop.js";
@@ -8,6 +8,8 @@ import {BlockError, BlockErrorCode, isBlockErrorAborted} from "../errors/index.j
8
8
  import {BlockProcessOpts} from "../options.js";
9
9
  import {IBlockInput} from "./blockInput/types.js";
10
10
  import {importBlock} from "./importBlock.js";
11
+ import {importExecutionPayload} from "./importExecutionPayload.js";
12
+ import {PayloadEnvelopeInput} from "./payloadEnvelopeInput/payloadEnvelopeInput.js";
11
13
  import {FullyVerifiedBlock, ImportBlockOpts} from "./types.js";
12
14
  import {assertLinearChainSegment} from "./utils/chainSegment.js";
13
15
  import {verifyBlocksInEpoch} from "./verifyBlock.js";
@@ -21,20 +23,24 @@ const QUEUE_MAX_LENGTH = 256;
21
23
  * BlockProcessor processes block jobs in a queued fashion, one after the other.
22
24
  */
23
25
  export class BlockProcessor {
24
- readonly jobQueue: JobItemQueue<[IBlockInput[], ImportBlockOpts], void>;
26
+ readonly jobQueue: JobItemQueue<[IBlockInput[], Map<Slot, PayloadEnvelopeInput> | null, ImportBlockOpts], void>;
25
27
 
26
28
  constructor(chain: BeaconChain, metrics: Metrics | null, opts: BlockProcessOpts, signal: AbortSignal) {
27
- this.jobQueue = new JobItemQueue<[IBlockInput[], ImportBlockOpts], void>(
28
- (job, importOpts) => {
29
- return processBlocks.call(chain, job, {...opts, ...importOpts});
29
+ this.jobQueue = new JobItemQueue<[IBlockInput[], Map<Slot, PayloadEnvelopeInput> | null, ImportBlockOpts], void>(
30
+ (job, payloadEnvelopes, importOpts) => {
31
+ return processBlocks.call(chain, job, payloadEnvelopes, {...opts, ...importOpts});
30
32
  },
31
33
  {maxLength: QUEUE_MAX_LENGTH, noYieldIfOneItem: true, signal},
32
34
  metrics?.blockProcessorQueue ?? undefined
33
35
  );
34
36
  }
35
37
 
36
- async processBlocksJob(job: IBlockInput[], opts: ImportBlockOpts = {}): Promise<void> {
37
- await this.jobQueue.push(job, opts);
38
+ async processBlocksJob(
39
+ job: IBlockInput[],
40
+ payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
41
+ opts: ImportBlockOpts = {}
42
+ ): Promise<void> {
43
+ await this.jobQueue.push(job, payloadEnvelopes, opts);
38
44
  }
39
45
  }
40
46
 
@@ -51,16 +57,13 @@ export class BlockProcessor {
51
57
  export async function processBlocks(
52
58
  this: BeaconChain,
53
59
  blocks: IBlockInput[],
60
+ payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
54
61
  opts: BlockProcessOpts & ImportBlockOpts
55
62
  ): Promise<void> {
56
63
  if (blocks.length === 0) {
57
64
  return; // TODO: or throw?
58
65
  }
59
66
 
60
- if (blocks.length > 1) {
61
- assertLinearChainSegment(this.config, blocks);
62
- }
63
-
64
67
  try {
65
68
  const {relevantBlocks, parentSlots, parentBlock} = verifyBlocksSanityChecks(this, blocks, opts);
66
69
 
@@ -70,10 +73,25 @@ export async function processBlocks(
70
73
  return;
71
74
  }
72
75
 
76
+ const {warnings: orphanedPayloads} = assertLinearChainSegment(
77
+ this.config,
78
+ relevantBlocks,
79
+ payloadEnvelopes,
80
+ parentBlock
81
+ );
82
+ if (orphanedPayloads != null) {
83
+ for (const orphaned of orphanedPayloads) {
84
+ this.logger.debug("Orphaned payload envelope in chain segment", {
85
+ slot: orphaned.slot,
86
+ blockRoot: orphaned.payloadEnvelopeInput.blockRootHex,
87
+ });
88
+ }
89
+ }
90
+
73
91
  // Fully verify a block to be imported immediately after. Does not produce any side-effects besides adding intermediate
74
92
  // states in the state cache through regen.
75
93
  const {postStates, dataAvailabilityStatuses, proposerBalanceDeltas, segmentExecStatus, indexedAttestationsByBlock} =
76
- await verifyBlocksInEpoch.call(this, parentBlock, relevantBlocks, opts);
94
+ await verifyBlocksInEpoch.call(this, parentBlock, relevantBlocks, payloadEnvelopes, opts);
77
95
 
78
96
  // If segmentExecStatus has lvhForkchoice then, the entire segment should be invalid
79
97
  // and we need to further propagate
@@ -103,6 +121,20 @@ export async function processBlocks(
103
121
  for (const fullyVerifiedBlock of fullyVerifiedBlocks) {
104
122
  // TODO: Consider batching importBlock too if it takes significant time
105
123
  await importBlock.call(this, fullyVerifiedBlock, opts);
124
+
125
+ const slot = fullyVerifiedBlock.blockInput.getBlock().message.slot;
126
+ const payloadInput = payloadEnvelopes?.get(slot);
127
+ if (payloadInput?.hasPayloadEnvelope()) {
128
+ if (!payloadInput.isComplete()) {
129
+ // we validated DA before reaching this
130
+ throw new Error(`Payload envelope for slot ${slot} not complete after DA verification`);
131
+ }
132
+ // we already awaited DA in verifyBlocksInEpoch for this segment
133
+ // TODO GLOAS: may need FullyVerifiedPayload here with DatAvailabilityStatus added from here
134
+ // the current flow use that data from the forkchoice pending node which is not correct
135
+ await importExecutionPayload.call(this, payloadInput, {validSignature: false});
136
+ }
137
+
106
138
  await nextEventLoop();
107
139
  }
108
140
  } catch (e) {
@@ -2,7 +2,7 @@ import {Metrics} from "../../metrics/metrics.js";
2
2
  import {JobItemQueue} from "../../util/queue/index.js";
3
3
  import type {BeaconChain} from "../chain.js";
4
4
  import {PayloadEnvelopeInput} from "../seenCache/seenPayloadEnvelopeInput.js";
5
- import {importExecutionPayload} from "./importExecutionPayload.js";
5
+ import {processExecutionPayload} from "./importExecutionPayload.js";
6
6
  import {ImportPayloadOpts} from "./types.js";
7
7
 
8
8
  // TODO GLOAS: Set to be equal to DEFAULT_MAX_PENDING_UNFINALIZED_PAYLOAD_ENVELOPE_WRITES for now
@@ -30,7 +30,7 @@ export class PayloadEnvelopeProcessor {
30
30
  this.jobQueue = new JobItemQueue<[PayloadEnvelopeInput, ImportPayloadOpts], void>(
31
31
  (payloadInput, opts) => {
32
32
  this.importStatus.set(payloadInput, PayloadEnvelopeImportStatus.importing);
33
- return importExecutionPayload.call(chain, payloadInput, signal, opts);
33
+ return processExecutionPayload.call(chain, payloadInput, signal, opts);
34
34
  },
35
35
  {maxLength: QUEUE_MAX_LENGTH, noYieldIfOneItem: true, signal},
36
36
  metrics?.payloadEnvelopeProcessorQueue ?? undefined
@@ -1,5 +1,5 @@
1
1
  import type {ChainForkConfig} from "@lodestar/config";
2
- import {BlockExecutionStatus} from "@lodestar/fork-choice";
2
+ import type {BlockExecutionStatus, PayloadExecutionStatus} from "@lodestar/fork-choice";
3
3
  import {ForkSeq} from "@lodestar/params";
4
4
  import {DataAvailabilityStatus, IBeaconStateView, computeEpochAtSlot} from "@lodestar/state-transition";
5
5
  import type {IndexedAttestation, Slot, fulu} from "@lodestar/types";
@@ -107,5 +107,5 @@ export type FullyVerifiedBlock = {
107
107
  /** Seen timestamp seconds */
108
108
  seenTimestampSec: number;
109
109
  /** If the execution payload couldn't be verified because of EL syncing status, used in optimistic sync */
110
- executionStatus: BlockExecutionStatus;
110
+ executionStatus: BlockExecutionStatus | PayloadExecutionStatus;
111
111
  };
@@ -1,29 +1,118 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
- import {ssz} from "@lodestar/types";
2
+ import {ProtoBlock} from "@lodestar/fork-choice";
3
+ import {Slot, isGloasBeaconBlock, ssz} from "@lodestar/types";
4
+ import {toRootHex} from "@lodestar/utils";
3
5
  import {BlockError, BlockErrorCode} from "../../errors/index.js";
4
6
  import {IBlockInput} from "../blockInput/types.js";
7
+ import {PayloadEnvelopeInput} from "../payloadEnvelopeInput/payloadEnvelopeInput.js";
8
+
9
+ export type OrphanedPayloadEnvelope = {
10
+ slot: Slot;
11
+ payloadEnvelopeInput: PayloadEnvelopeInput;
12
+ };
13
+
14
+ export type ChainSegmentResult = {warnings: OrphanedPayloadEnvelope[] | null};
5
15
 
6
16
  /**
7
- * Assert this chain segment of blocks is linear with slot numbers and hashes
17
+ * Assert this chain segment of blocks is linear with slot numbers and hashes,
18
+ * and that the provided envelopes are consistent with their respective blocks.
19
+ *
20
+ * Must be called after verifyBlocksSanityChecks so that parentBlock (from forkchoice)
21
+ * is available to seed the execution hash chain.
22
+ *
23
+ * For each block:
24
+ * - Verifies parent root + slot linearity
25
+ * - For gloas: verifies bid.parentBlockHash matches the tracked execution hash; if not, the
26
+ * previous FULL envelope is treated as orphaned (segment continues as if previous slot was EMPTY)
27
+ * - If an envelope exists for this slot: verifies it references this block's root
28
+ * - Advances the tracked execution hash (FULL if envelope present, EMPTY if not)
8
29
  */
30
+ export function assertLinearChainSegment(
31
+ config: ChainForkConfig,
32
+ blocks: IBlockInput[],
33
+ payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
34
+ parentBlock: ProtoBlock
35
+ ): ChainSegmentResult {
36
+ const warnings: OrphanedPayloadEnvelope[] = [];
9
37
 
10
- export function assertLinearChainSegment(config: ChainForkConfig, blocks: IBlockInput[]): void {
11
- for (let i = 0; i < blocks.length - 1; i++) {
38
+ // Track the expected execution payload block hash through the segment.
39
+ // Starts from the known forkchoice parent's execution hash.
40
+ // - FULL variant (envelope present for slot): advances to envelope.payload.blockHash
41
+ // - EMPTY variant (no envelope for slot): execution hash is unchanged
42
+ // null only for pre-merge parents, which cannot precede gloas blocks.
43
+ let currentExecHash: string | null = parentBlock.executionPayloadBlockHash;
44
+ // Track the execution hash before the last FULL advancement so we can recover
45
+ // if the next block reveals that envelope was orphaned.
46
+ let prevExecHash: string | null = currentExecHash;
47
+ // The slot whose envelope last advanced currentExecHash (for warning context).
48
+ let lastFullSlot: Slot | null = null;
49
+
50
+ for (let i = 0; i < blocks.length; i++) {
12
51
  const block = blocks[i].getBlock();
13
- const child = blocks[i + 1].getBlock();
14
- // If this block has a child in this chain segment, ensure that its parent root matches
15
- // the root of this block.
16
- if (
17
- !ssz.Root.equals(
18
- config.getForkTypes(block.message.slot).BeaconBlock.hashTreeRoot(block.message),
19
- child.message.parentRoot
20
- )
21
- ) {
22
- throw new BlockError(block, {code: BlockErrorCode.NON_LINEAR_PARENT_ROOTS});
52
+ const slot = block.message.slot;
53
+
54
+ if (i > 0) {
55
+ const prevBlock = blocks[i - 1].getBlock();
56
+ // Ensure parent root matches the previous block's root
57
+ if (
58
+ !ssz.Root.equals(
59
+ config.getForkTypes(prevBlock.message.slot).BeaconBlock.hashTreeRoot(prevBlock.message),
60
+ block.message.parentRoot
61
+ )
62
+ ) {
63
+ throw new BlockError(block, {code: BlockErrorCode.NON_LINEAR_PARENT_ROOTS});
64
+ }
65
+ // Ensure slots are strictly increasing
66
+ if (slot <= prevBlock.message.slot) {
67
+ throw new BlockError(block, {code: BlockErrorCode.NON_LINEAR_SLOTS});
68
+ }
23
69
  }
24
- // Ensure that the slots are strictly increasing throughout the chain segment.
25
- if (child.message.slot <= block.message.slot) {
26
- throw new BlockError(block, {code: BlockErrorCode.NON_LINEAR_SLOTS});
70
+
71
+ if (isGloasBeaconBlock(block.message) && currentExecHash !== null) {
72
+ // Verify the bid's parentBlockHash matches the tracked execution hash.
73
+ // This ensures the block was built on the correct FULL or EMPTY variant of its parent.
74
+ const bidParentHash = toRootHex(block.message.body.signedExecutionPayloadBid.message.parentBlockHash);
75
+ if (bidParentHash !== currentExecHash) {
76
+ // Maybe the previous slot's FULL envelope was orphaned — try falling back.
77
+ // If even prevExecHash doesn't match, the segment is non-linear.
78
+ if (bidParentHash !== prevExecHash) {
79
+ throw new BlockError(block, {
80
+ code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN,
81
+ parentRoot: toRootHex(block.message.parentRoot),
82
+ parentBlockHash: bidParentHash,
83
+ });
84
+ }
85
+ if (lastFullSlot !== null && payloadEnvelopes !== null) {
86
+ const orphanedInput = payloadEnvelopes.get(lastFullSlot);
87
+ if (orphanedInput != null) {
88
+ warnings.push({slot: lastFullSlot, payloadEnvelopeInput: orphanedInput});
89
+ }
90
+ }
91
+ currentExecHash = prevExecHash;
92
+ }
93
+
94
+ const payloadInput = payloadEnvelopes?.get(slot) ?? null;
95
+ const payloadEnvelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : null;
96
+ if (payloadEnvelope !== null) {
97
+ // Verify the envelope references this block's root
98
+ const blockRoot = toRootHex(config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block.message));
99
+ const envelopeBlockRoot = toRootHex(payloadEnvelope.message.beaconBlockRoot);
100
+ if (blockRoot !== envelopeBlockRoot) {
101
+ throw new BlockError(block, {
102
+ code: BlockErrorCode.ENVELOPE_BLOCK_ROOT_MISMATCH,
103
+ envelopeBlockRoot,
104
+ blockRoot,
105
+ });
106
+ }
107
+
108
+ // FULL variant: save state before advancing, then advance
109
+ prevExecHash = currentExecHash;
110
+ lastFullSlot = slot;
111
+ currentExecHash = toRootHex(payloadEnvelope.message.payload.blockHash);
112
+ }
113
+ // EMPTY variant: currentExecHash unchanged
27
114
  }
28
115
  }
116
+
117
+ return {warnings: warnings.length > 0 ? warnings : null};
29
118
  }
@@ -1,12 +1,14 @@
1
1
  import {ExecutionStatus, ProtoBlock} from "@lodestar/fork-choice";
2
- import {ForkName, isForkPostFulu} from "@lodestar/params";
2
+ import {ForkName, ForkSeq, isForkPostFulu} from "@lodestar/params";
3
3
  import {DataAvailabilityStatus, IBeaconStateView, computeEpochAtSlot} from "@lodestar/state-transition";
4
- import {IndexedAttestation, deneb} from "@lodestar/types";
4
+ import {IndexedAttestation, Slot, deneb} from "@lodestar/types";
5
+ import {getBlobKzgCommitments} from "../../util/dataColumns.js";
5
6
  import type {BeaconChain} from "../chain.js";
6
7
  import {BlockError, BlockErrorCode} from "../errors/index.js";
7
8
  import {BlockProcessOpts} from "../options.js";
8
9
  import {RegenCaller} from "../regen/index.js";
9
10
  import {DAType, IBlockInput} from "./blockInput/index.js";
11
+ import {PayloadEnvelopeInput} from "./payloadEnvelopeInput/payloadEnvelopeInput.js";
10
12
  import {ImportBlockOpts} from "./types.js";
11
13
  import {DENEB_BLOWFISH_BANNER} from "./utils/blowfishBanner.js";
12
14
  import {ELECTRA_GIRAFFE_BANNER} from "./utils/giraffeBanner.js";
@@ -16,6 +18,7 @@ import {verifyBlocksDataAvailability} from "./verifyBlocksDataAvailability.js";
16
18
  import {SegmentExecStatus, verifyBlocksExecutionPayload} from "./verifyBlocksExecutionPayloads.js";
17
19
  import {verifyBlocksSignatures} from "./verifyBlocksSignatures.js";
18
20
  import {verifyBlocksStateTransitionOnly} from "./verifyBlocksStateTransitionOnly.js";
21
+ import {verifyPayloadsDataAvailability} from "./verifyPayloadsDataAvailability.js";
19
22
 
20
23
  /**
21
24
  * Verifies 1 or more blocks are fully valid; from a linear sequence of blocks.
@@ -32,6 +35,7 @@ export async function verifyBlocksInEpoch(
32
35
  this: BeaconChain,
33
36
  parentBlock: ProtoBlock,
34
37
  blockInputs: IBlockInput[],
38
+ payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
35
39
  opts: BlockProcessOpts & ImportBlockOpts
36
40
  ): Promise<{
37
41
  postStates: IBeaconStateView[];
@@ -110,6 +114,26 @@ export async function verifyBlocksInEpoch(
110
114
  });
111
115
  }
112
116
 
117
+ // Pick the data-availability source by fork:
118
+ // - Pre-Gloas: blob/Fulu-column data lives in IBlockInput → verifyBlocksDataAvailability.
119
+ // - Post-Gloas: verifyPayloadsDataAvailability
120
+ const daAvailabilityPromise =
121
+ fork >= ForkSeq.gloas
122
+ ? (async () => {
123
+ const payloadInputsForDa: PayloadEnvelopeInput[] = [];
124
+ for (const input of blockInputs) {
125
+ const pi = payloadEnvelopes?.get(input.slot);
126
+ if (pi !== undefined) payloadInputsForDa.push(pi);
127
+ }
128
+ await verifyPayloadsDataAvailability(payloadInputsForDa, abortController.signal);
129
+ return {
130
+ // post-gloas, DataAvailabilityStatus is NotRequired for forkChoice.onBlock() ProtoBlock
131
+ dataAvailabilityStatuses: blockInputs.map(() => DataAvailabilityStatus.NotRequired),
132
+ availableTime: Date.now(),
133
+ };
134
+ })()
135
+ : verifyBlocksDataAvailability(blockInputs, abortController.signal);
136
+
113
137
  // batch all I/O operations to reduce overhead
114
138
  const [
115
139
  segmentExecStatus,
@@ -119,8 +143,8 @@ export async function verifyBlocksInEpoch(
119
143
  ] = await Promise.all([
120
144
  verifyExecutionPayloadsPromise,
121
145
 
122
- // data availability for the blobs
123
- verifyBlocksDataAvailability(blockInputs, abortController.signal),
146
+ // data availability (fork-specific; see daAvailabilityPromise above)
147
+ daAvailabilityPromise,
124
148
 
125
149
  // Run state transition only
126
150
  // TODO: Ensure it yields to allow flushing to workers and engine API
@@ -149,6 +173,9 @@ export async function verifyBlocksInEpoch(
149
173
  opts
150
174
  )
151
175
  : Promise.resolve({verifySignaturesTime: Date.now()}),
176
+
177
+ // TODO GLOAS: can verify payload signatures in batch too
178
+ // maybe chain with the above verifyBlocksSignatures()
152
179
  ]);
153
180
 
154
181
  if (opts.verifyOnly !== true) {
@@ -200,7 +227,9 @@ export async function verifyBlocksInEpoch(
200
227
  blockInputs.length === 1 &&
201
228
  // gossip blocks have seenTimestampSec
202
229
  opts.seenTimestampSec !== undefined &&
230
+ // PreData (pre-deneb) and NoData (gloas) carry no blob data on the block — skip metric
203
231
  blockInputs[0].type !== DAType.PreData &&
232
+ blockInputs[0].type !== DAType.NoData &&
204
233
  executionStatuses[0] === ExecutionStatus.Valid
205
234
  ) {
206
235
  // Find the max time when the block was actually verified
@@ -209,8 +238,8 @@ export async function verifyBlocksInEpoch(
209
238
  this.metrics?.gossipBlock.receivedToFullyVerifiedTime.observe(recvTofullyVerifedTime);
210
239
 
211
240
  const verifiedToBlobsAvailabiltyTime = Math.max(availableTime - fullyVerifiedTime, 0) / 1000;
212
- const block = blockInputs[0].getBlock() as deneb.SignedBeaconBlock;
213
- const numBlobs = block.message.body.blobKzgCommitments.length;
241
+ const block = blockInputs[0].getBlock();
242
+ const numBlobs = getBlobKzgCommitments(blockInputs[0].forkName, block as deneb.SignedBeaconBlock).length;
214
243
 
215
244
  this.metrics?.gossipBlock.verifiedToBlobsAvailabiltyTime.observe({numBlobs}, verifiedToBlobsAvailabiltyTime);
216
245
  this.logger.verbose("Verified blockInput fully with blobs availability", {
@@ -90,15 +90,24 @@ export function verifyBlocksSanityChecks(
90
90
  } else {
91
91
  // When importing a block segment, only the first NON-IGNORED block must be known to the fork-choice.
92
92
  const parentRoot = toRootHex(block.message.parentRoot);
93
- parentBlock = isGloasBeaconBlock(block.message)
94
- ? chain.forkChoice.getBlockHexAndBlockHash(
95
- parentRoot,
96
- toRootHex(block.message.body.signedExecutionPayloadBid.message.parentBlockHash)
97
- )
98
- : chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
99
- if (!parentBlock) {
93
+ const parentBlockDefaultStatus = chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
94
+ if (!parentBlockDefaultStatus) {
100
95
  throw new BlockError(block, {code: BlockErrorCode.PARENT_UNKNOWN, parentRoot});
101
96
  }
97
+
98
+ parentBlock = parentBlockDefaultStatus;
99
+ if (isGloasBeaconBlock(block.message)) {
100
+ const parentBlockHash = toRootHex(block.message.body.signedExecutionPayloadBid.message.parentBlockHash);
101
+ const parentBlockWithPayload = chain.forkChoice.getBlockHexAndBlockHash(parentRoot, parentBlockHash);
102
+ if (!parentBlockWithPayload) {
103
+ throw new BlockError(block, {
104
+ code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN,
105
+ parentRoot,
106
+ parentBlockHash,
107
+ });
108
+ }
109
+ parentBlock = parentBlockWithPayload;
110
+ }
102
111
  // Parent is known to the fork-choice
103
112
  parentBlockSlot = parentBlock.slot;
104
113
  }
@@ -32,8 +32,8 @@ export function verifyExecutionPayloadEnvelope(
32
32
  const payload = envelope.payload;
33
33
 
34
34
  // Verify consistency with the beacon block.
35
- // Compute header root on a copy of latestBlockHeader to avoid mutating state.
36
- const headerValue = {...state.latestBlockHeader};
35
+ // Compute header root on a clone of latestBlockHeader to avoid mutating state.
36
+ const headerValue = ssz.phase0.BeaconBlockHeader.clone(state.latestBlockHeader);
37
37
  if (byteArrayEquals(headerValue.stateRoot, ssz.Root.defaultValue())) {
38
38
  headerValue.stateRoot = state.hashTreeRoot();
39
39
  }
@@ -106,6 +106,7 @@ import {
106
106
  SeenExecutionPayloadBids,
107
107
  SeenPayloadAttesters,
108
108
  SeenPayloadEnvelopeInput,
109
+ SeenProposerPreferences,
109
110
  SeenSyncCommitteeMessages,
110
111
  } from "./seenCache/index.js";
111
112
  import {SeenAggregatedAttestations} from "./seenCache/seenAggregateAndProof.js";
@@ -186,6 +187,7 @@ export class BeaconChain implements IBeaconChain {
186
187
  readonly seenPayloadAttesters = new SeenPayloadAttesters();
187
188
  readonly seenAggregatedAttestations: SeenAggregatedAttestations;
188
189
  readonly seenExecutionPayloadBids = new SeenExecutionPayloadBids();
190
+ readonly seenProposerPreferences = new SeenProposerPreferences();
189
191
  readonly seenBlockProposers = new SeenBlockProposers();
190
192
  readonly seenSyncCommitteeMessages = new SeenSyncCommitteeMessages();
191
193
  readonly seenContributionAndProof: SeenContributionAndProof;
@@ -891,6 +893,10 @@ export class BeaconChain implements IBeaconChain {
891
893
  parentBlockSlot: Slot,
892
894
  parentBlockRootHex: RootHex
893
895
  ): Promise<electra.ExecutionRequests> {
896
+ // at the fork boundary, parent is pre-gloas
897
+ if (!isForkPostGloas(this.config.getForkName(parentBlockSlot))) {
898
+ return ssz.electra.ExecutionRequests.defaultValue();
899
+ }
894
900
  const envelope = await this.getExecutionPayloadEnvelope(parentBlockSlot, parentBlockRootHex);
895
901
  if (envelope === null) {
896
902
  throw Error(`Parent execution payload envelope not found slot=${parentBlockSlot}, root=${parentBlockRootHex}`);
@@ -1094,11 +1100,15 @@ export class BeaconChain implements IBeaconChain {
1094
1100
  }
1095
1101
 
1096
1102
  async processBlock(block: IBlockInput, opts?: ImportBlockOpts): Promise<void> {
1097
- return this.blockProcessor.processBlocksJob([block], opts);
1103
+ return this.blockProcessor.processBlocksJob([block], null, opts);
1098
1104
  }
1099
1105
 
1100
- async processChainSegment(blocks: IBlockInput[], opts?: ImportBlockOpts): Promise<void> {
1101
- return this.blockProcessor.processBlocksJob(blocks, opts);
1106
+ async processChainSegment(
1107
+ blocks: IBlockInput[],
1108
+ payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
1109
+ opts?: ImportBlockOpts
1110
+ ): Promise<void> {
1111
+ await this.blockProcessor.processBlocksJob(blocks, payloadEnvelopes, opts);
1102
1112
  }
1103
1113
 
1104
1114
  async processExecutionPayload(payloadInput: PayloadEnvelopeInput, opts?: ImportPayloadOpts): Promise<void> {
@@ -1429,6 +1439,7 @@ export class BeaconChain implements IBeaconChain {
1429
1439
  this.payloadAttestationPool.prune(slot);
1430
1440
  this.executionPayloadBidPool.prune(slot);
1431
1441
  this.seenExecutionPayloadBids.prune(slot);
1442
+ this.seenProposerPreferences.prune(slot);
1432
1443
  this.seenAttestationDatas.onSlot(slot);
1433
1444
  this.reprocessController.onSlot(slot);
1434
1445