@lodestar/beacon-node 1.43.0-dev.dfb984e779 → 1.43.0-dev.e3c96e7a79

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 (224) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +13 -3
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/pool/index.js +45 -2
  6. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  7. package/lib/api/impl/debug/index.d.ts.map +1 -1
  8. package/lib/api/impl/debug/index.js +0 -1
  9. package/lib/api/impl/debug/index.js.map +1 -1
  10. package/lib/api/impl/validator/index.d.ts.map +1 -1
  11. package/lib/api/impl/validator/index.js +68 -2
  12. package/lib/api/impl/validator/index.js.map +1 -1
  13. package/lib/chain/blocks/blockInput/blockInput.d.ts +3 -0
  14. package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
  15. package/lib/chain/blocks/blockInput/blockInput.js +4 -1
  16. package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
  17. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  18. package/lib/chain/blocks/importBlock.js +16 -31
  19. package/lib/chain/blocks/importBlock.js.map +1 -1
  20. package/lib/chain/blocks/importExecutionPayload.d.ts +5 -3
  21. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  22. package/lib/chain/blocks/importExecutionPayload.js +25 -14
  23. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  24. package/lib/chain/blocks/index.d.ts.map +1 -1
  25. package/lib/chain/blocks/index.js +36 -21
  26. package/lib/chain/blocks/index.js.map +1 -1
  27. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +4 -0
  28. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  29. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +9 -2
  30. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  31. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +1 -0
  32. package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
  33. package/lib/chain/blocks/types.d.ts +2 -1
  34. package/lib/chain/blocks/types.d.ts.map +1 -1
  35. package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
  36. package/lib/chain/blocks/utils/chainSegment.js +8 -0
  37. package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
  38. package/lib/chain/blocks/verifyBlock.d.ts +2 -1
  39. package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
  40. package/lib/chain/blocks/verifyBlock.js +30 -12
  41. package/lib/chain/blocks/verifyBlock.js.map +1 -1
  42. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts +0 -4
  43. package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts.map +1 -1
  44. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +5 -2
  45. package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
  46. package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts +2 -1
  47. package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
  48. package/lib/chain/blocks/verifyBlocksSanityChecks.js +16 -7
  49. package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
  50. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
  51. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
  52. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +5 -2
  53. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
  54. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -1
  55. package/lib/chain/blocks/verifyPayloadsDataAvailability.js +8 -3
  56. package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -1
  57. package/lib/chain/chain.d.ts +2 -1
  58. package/lib/chain/chain.d.ts.map +1 -1
  59. package/lib/chain/chain.js +13 -8
  60. package/lib/chain/chain.js.map +1 -1
  61. package/lib/chain/emitter.d.ts +0 -11
  62. package/lib/chain/emitter.d.ts.map +1 -1
  63. package/lib/chain/emitter.js +0 -4
  64. package/lib/chain/emitter.js.map +1 -1
  65. package/lib/chain/errors/index.d.ts +1 -0
  66. package/lib/chain/errors/index.d.ts.map +1 -1
  67. package/lib/chain/errors/index.js +1 -0
  68. package/lib/chain/errors/index.js.map +1 -1
  69. package/lib/chain/errors/proposerPreferences.d.ts +40 -0
  70. package/lib/chain/errors/proposerPreferences.d.ts.map +1 -0
  71. package/lib/chain/errors/proposerPreferences.js +14 -0
  72. package/lib/chain/errors/proposerPreferences.js.map +1 -0
  73. package/lib/chain/initState.d.ts.map +1 -1
  74. package/lib/chain/initState.js +6 -1
  75. package/lib/chain/initState.js.map +1 -1
  76. package/lib/chain/interface.d.ts +2 -1
  77. package/lib/chain/interface.d.ts.map +1 -1
  78. package/lib/chain/interface.js.map +1 -1
  79. package/lib/chain/opPools/payloadAttestationPool.d.ts +3 -2
  80. package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
  81. package/lib/chain/opPools/payloadAttestationPool.js +26 -4
  82. package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
  83. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  84. package/lib/chain/prepareNextSlot.js +16 -18
  85. package/lib/chain/prepareNextSlot.js.map +1 -1
  86. package/lib/chain/produceBlock/produceBlockBody.d.ts +12 -3
  87. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  88. package/lib/chain/produceBlock/produceBlockBody.js +34 -22
  89. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  90. package/lib/chain/regen/interface.d.ts +1 -0
  91. package/lib/chain/regen/interface.d.ts.map +1 -1
  92. package/lib/chain/regen/interface.js +1 -0
  93. package/lib/chain/regen/interface.js.map +1 -1
  94. package/lib/chain/regen/queued.d.ts.map +1 -1
  95. package/lib/chain/regen/queued.js +1 -4
  96. package/lib/chain/regen/queued.js.map +1 -1
  97. package/lib/chain/regen/regen.d.ts.map +1 -1
  98. package/lib/chain/regen/regen.js +1 -4
  99. package/lib/chain/regen/regen.js.map +1 -1
  100. package/lib/chain/seenCache/index.d.ts +1 -0
  101. package/lib/chain/seenCache/index.d.ts.map +1 -1
  102. package/lib/chain/seenCache/index.js +1 -0
  103. package/lib/chain/seenCache/index.js.map +1 -1
  104. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +14 -5
  105. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  106. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +49 -15
  107. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  108. package/lib/chain/seenCache/seenProposerPreferences.d.ts +16 -0
  109. package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +1 -0
  110. package/lib/chain/seenCache/seenProposerPreferences.js +26 -0
  111. package/lib/chain/seenCache/seenProposerPreferences.js.map +1 -0
  112. package/lib/chain/validation/executionPayloadBid.js +11 -8
  113. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  114. package/lib/chain/validation/proposerPreferences.d.ts +8 -0
  115. package/lib/chain/validation/proposerPreferences.d.ts.map +1 -0
  116. package/lib/chain/validation/proposerPreferences.js +91 -0
  117. package/lib/chain/validation/proposerPreferences.js.map +1 -0
  118. package/lib/network/gossip/interface.d.ts +7 -1
  119. package/lib/network/gossip/interface.d.ts.map +1 -1
  120. package/lib/network/gossip/interface.js +1 -0
  121. package/lib/network/gossip/interface.js.map +1 -1
  122. package/lib/network/gossip/scoringParameters.d.ts.map +1 -1
  123. package/lib/network/gossip/scoringParameters.js +12 -1
  124. package/lib/network/gossip/scoringParameters.js.map +1 -1
  125. package/lib/network/gossip/topic.d.ts +10 -0
  126. package/lib/network/gossip/topic.d.ts.map +1 -1
  127. package/lib/network/gossip/topic.js +6 -0
  128. package/lib/network/gossip/topic.js.map +1 -1
  129. package/lib/network/interface.d.ts +1 -0
  130. package/lib/network/interface.d.ts.map +1 -1
  131. package/lib/network/network.d.ts +1 -0
  132. package/lib/network/network.d.ts.map +1 -1
  133. package/lib/network/network.js +5 -0
  134. package/lib/network/network.js.map +1 -1
  135. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  136. package/lib/network/processor/gossipHandlers.js +34 -10
  137. package/lib/network/processor/gossipHandlers.js.map +1 -1
  138. package/lib/network/processor/gossipQueues/index.d.ts.map +1 -1
  139. package/lib/network/processor/gossipQueues/index.js +5 -0
  140. package/lib/network/processor/gossipQueues/index.js.map +1 -1
  141. package/lib/network/processor/index.d.ts.map +1 -1
  142. package/lib/network/processor/index.js +6 -5
  143. package/lib/network/processor/index.js.map +1 -1
  144. package/lib/node/nodejs.js +2 -2
  145. package/lib/node/nodejs.js.map +1 -1
  146. package/lib/node/notifier.js +1 -7
  147. package/lib/node/notifier.js.map +1 -1
  148. package/lib/sync/constants.d.ts +3 -1
  149. package/lib/sync/constants.d.ts.map +1 -1
  150. package/lib/sync/constants.js +3 -4
  151. package/lib/sync/constants.js.map +1 -1
  152. package/lib/sync/range/batch.d.ts +16 -0
  153. package/lib/sync/range/batch.d.ts.map +1 -1
  154. package/lib/sync/range/batch.js +97 -21
  155. package/lib/sync/range/batch.js.map +1 -1
  156. package/lib/sync/range/chain.d.ts +6 -0
  157. package/lib/sync/range/chain.d.ts.map +1 -1
  158. package/lib/sync/range/chain.js +50 -7
  159. package/lib/sync/range/chain.js.map +1 -1
  160. package/lib/sync/unknownBlock.d.ts +0 -2
  161. package/lib/sync/unknownBlock.d.ts.map +1 -1
  162. package/lib/sync/unknownBlock.js +2 -47
  163. package/lib/sync/unknownBlock.js.map +1 -1
  164. package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
  165. package/lib/sync/utils/downloadByRange.js +36 -21
  166. package/lib/sync/utils/downloadByRange.js.map +1 -1
  167. package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
  168. package/lib/sync/utils/downloadByRoot.js +10 -0
  169. package/lib/sync/utils/downloadByRoot.js.map +1 -1
  170. package/lib/util/sszBytes.d.ts.map +1 -1
  171. package/lib/util/sszBytes.js +8 -6
  172. package/lib/util/sszBytes.js.map +1 -1
  173. package/package.json +15 -15
  174. package/src/api/impl/beacon/blocks/index.ts +16 -3
  175. package/src/api/impl/beacon/pool/index.ts +83 -1
  176. package/src/api/impl/debug/index.ts +0 -1
  177. package/src/api/impl/validator/index.ts +82 -1
  178. package/src/chain/blocks/blockInput/blockInput.ts +4 -1
  179. package/src/chain/blocks/importBlock.ts +16 -50
  180. package/src/chain/blocks/importExecutionPayload.ts +33 -14
  181. package/src/chain/blocks/index.ts +34 -15
  182. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +10 -2
  183. package/src/chain/blocks/payloadEnvelopeInput/types.ts +1 -0
  184. package/src/chain/blocks/types.ts +2 -1
  185. package/src/chain/blocks/utils/chainSegment.ts +8 -0
  186. package/src/chain/blocks/verifyBlock.ts +45 -13
  187. package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +6 -4
  188. package/src/chain/blocks/verifyBlocksSanityChecks.ts +16 -6
  189. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +7 -2
  190. package/src/chain/blocks/verifyPayloadsDataAvailability.ts +7 -4
  191. package/src/chain/chain.ts +15 -7
  192. package/src/chain/emitter.ts +0 -11
  193. package/src/chain/errors/index.ts +1 -0
  194. package/src/chain/errors/proposerPreferences.ts +47 -0
  195. package/src/chain/initState.ts +9 -1
  196. package/src/chain/interface.ts +2 -0
  197. package/src/chain/opPools/payloadAttestationPool.ts +29 -8
  198. package/src/chain/prepareNextSlot.ts +21 -29
  199. package/src/chain/produceBlock/produceBlockBody.ts +45 -27
  200. package/src/chain/regen/interface.ts +1 -0
  201. package/src/chain/regen/queued.ts +2 -7
  202. package/src/chain/regen/regen.ts +2 -7
  203. package/src/chain/seenCache/index.ts +1 -0
  204. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +67 -18
  205. package/src/chain/seenCache/seenProposerPreferences.ts +32 -0
  206. package/src/chain/validation/executionPayloadBid.ts +11 -8
  207. package/src/chain/validation/proposerPreferences.ts +110 -0
  208. package/src/network/gossip/interface.ts +6 -0
  209. package/src/network/gossip/scoringParameters.ts +14 -1
  210. package/src/network/gossip/topic.ts +6 -0
  211. package/src/network/interface.ts +1 -0
  212. package/src/network/network.ts +11 -0
  213. package/src/network/processor/gossipHandlers.ts +47 -11
  214. package/src/network/processor/gossipQueues/index.ts +5 -0
  215. package/src/network/processor/index.ts +6 -5
  216. package/src/node/nodejs.ts +2 -2
  217. package/src/node/notifier.ts +1 -8
  218. package/src/sync/constants.ts +4 -4
  219. package/src/sync/range/batch.ts +124 -24
  220. package/src/sync/range/chain.ts +57 -7
  221. package/src/sync/unknownBlock.ts +2 -50
  222. package/src/sync/utils/downloadByRange.ts +37 -21
  223. package/src/sync/utils/downloadByRoot.ts +12 -0
  224. package/src/util/sszBytes.ts +8 -6
@@ -9,14 +9,7 @@ import {
9
9
  NotReorgedReason,
10
10
  getSafeExecutionBlockHash,
11
11
  } from "@lodestar/fork-choice";
12
- import {
13
- ForkPostAltair,
14
- ForkPostElectra,
15
- ForkPostGloas,
16
- ForkSeq,
17
- MAX_SEED_LOOKAHEAD,
18
- SLOTS_PER_EPOCH,
19
- } from "@lodestar/params";
12
+ import {ForkPostAltair, ForkPostElectra, ForkSeq, MAX_SEED_LOOKAHEAD, SLOTS_PER_EPOCH} from "@lodestar/params";
20
13
  import {
21
14
  IBeaconStateView,
22
15
  RootCache,
@@ -27,17 +20,7 @@ import {
27
20
  isStatePostAltair,
28
21
  isStatePostBellatrix,
29
22
  } from "@lodestar/state-transition";
30
- import {
31
- Attestation,
32
- BeaconBlock,
33
- SignedBeaconBlock,
34
- altair,
35
- capella,
36
- electra,
37
- isGloasBeaconBlock,
38
- phase0,
39
- ssz,
40
- } from "@lodestar/types";
23
+ import {Attestation, BeaconBlock, altair, capella, electra, isGloasBeaconBlock, phase0, ssz} from "@lodestar/types";
41
24
  import {isErrorAborted, toRootHex} from "@lodestar/utils";
42
25
  import {ZERO_HASH_HEX} from "../../constants/index.js";
43
26
  import {callInNextEventLoop} from "../../util/eventLoop.js";
@@ -86,8 +69,8 @@ export async function importBlock(
86
69
  fullyVerifiedBlock: FullyVerifiedBlock,
87
70
  opts: ImportBlockOpts
88
71
  ): Promise<void> {
89
- const {blockInput, postState, parentBlockSlot, executionStatus, dataAvailabilityStatus, indexedAttestations} =
90
- fullyVerifiedBlock;
72
+ const {blockInput, postState, parentBlockSlot, dataAvailabilityStatus, indexedAttestations} = fullyVerifiedBlock;
73
+ let {executionStatus} = fullyVerifiedBlock;
91
74
  const block = blockInput.getBlock();
92
75
  const source = blockInput.getBlockSource();
93
76
  const {slot: blockSlot} = block.message;
@@ -122,13 +105,23 @@ export async function importBlock(
122
105
 
123
106
  // Should compute checkpoint balances before forkchoice.onBlock
124
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
+ }
125
119
  const blockSummary = this.forkChoice.onBlock(
126
120
  block.message,
127
121
  postState,
128
122
  blockDelaySec,
129
123
  currentSlot,
130
- fork >= ForkSeq.gloas ? ExecutionStatus.PayloadSeparated : executionStatus,
131
- // TODO GLOAS: this is not useful post-gloas, may need to remove it?
124
+ executionStatus,
132
125
  dataAvailabilityStatus
133
126
  );
134
127
 
@@ -136,33 +129,6 @@ export async function importBlock(
136
129
  // Some block event handlers require state being in state cache so need to do this before emitting EventType.block
137
130
  this.regen.processState(blockRootHex, postState);
138
131
 
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) {
142
- const payloadInput = this.seenPayloadEnvelopeInputCache.add({
143
- blockRootHex,
144
- block: block as SignedBeaconBlock<ForkPostGloas>,
145
- forkName: blockInput.forkName,
146
- sampledColumns: this.custodyConfig.sampledColumns,
147
- custodyColumns: this.custodyConfig.custodyColumns,
148
- timeCreatedSec: fullyVerifiedBlock.seenTimestampSec,
149
- });
150
- this.logger.debug("Created PayloadEnvelopeInput for block", {
151
- slot: blockSlot,
152
- root: blockRootHex,
153
- source: source.source,
154
- ...(opts.seenTimestampSec !== undefined ? {recvToImport: Date.now() / 1000 - opts.seenTimestampSec} : {}),
155
- });
156
-
157
- // Immediately attempt fetch of data columns from execution engine as the bid contains kzg commitments
158
- // which is all the information we need so there is no reason to delay until execution payload arrives
159
- // TODO GLOAS: If we want EL retries after this initial attempt, add an explicit retry policy here
160
- // (for example later in the slot). Do not couple retries to incoming gossip columns.
161
- // Columns fetched here feed payloadInput.addColumn, which resolves waitForAllData for any
162
- // in-flight importExecutionPayload. No processExecutionPayload trigger needed from this path.
163
- this.getBlobsTracker.triggerGetBlobs(payloadInput);
164
- }
165
-
166
132
  this.metrics?.importBlock.bySource.inc({source: source.source});
167
133
  this.logger.verbose("Added block to forkchoice and state cache", {slot: blockSlot, root: blockRootHex});
168
134
 
@@ -1,7 +1,8 @@
1
1
  import {routes} from "@lodestar/api";
2
- import {ExecutionStatus, PayloadExecutionStatus} from "@lodestar/fork-choice";
3
- import {isStatePostGloas} from "@lodestar/state-transition";
4
- import {fromHex} from "@lodestar/utils";
2
+ import {ExecutionStatus, PayloadExecutionStatus, getSafeExecutionBlockHash} from "@lodestar/fork-choice";
3
+ import {DataAvailabilityStatus, isStatePostGloas} from "@lodestar/state-transition";
4
+ import {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";
@@ -60,7 +61,6 @@ function toForkChoiceExecutionStatus(status: ExecutionPayloadStatus): PayloadExe
60
61
  switch (status) {
61
62
  case ExecutionPayloadStatus.VALID:
62
63
  return ExecutionStatus.Valid;
63
- // TODO GLOAS: Handle optimistic import for payload
64
64
  case ExecutionPayloadStatus.SYNCING:
65
65
  case ExecutionPayloadStatus.ACCEPTED:
66
66
  return ExecutionStatus.Syncing;
@@ -85,12 +85,14 @@ function toForkChoiceExecutionStatus(status: ExecutionPayloadStatus): PayloadExe
85
85
  * 4. Verify envelope (fields against state, signature, and EL in parallel where possible)
86
86
  * 5. Persist verified payload envelope to hot DB (waits for write-queue space for backpressure)
87
87
  * 6. Update fork choice (transitions the block's PENDING variant to FULL)
88
- * 7. Record metrics for payload envelope and column sources
89
- * 8. Emit `execution_payload` event
88
+ * 7. Queue notifyForkchoiceUpdate to engine api
89
+ * 8. Record metrics for payload envelope and column sources
90
+ * 9. Emit `execution_payload` event
90
91
  */
91
92
  export async function importExecutionPayload(
92
93
  this: BeaconChain,
93
94
  payloadInput: PayloadEnvelopeInput,
95
+ dataAvailabilityStatus: DataAvailabilityStatus,
94
96
  opts: ImportPayloadOpts = {}
95
97
  ): Promise<void> {
96
98
  const signedEnvelope = payloadInput.getPayloadEnvelope();
@@ -157,7 +159,7 @@ export async function importExecutionPayload(
157
159
  fork,
158
160
  envelope.payload,
159
161
  payloadInput.getVersionedHashes(),
160
- fromHex(protoBlock.parentRoot),
162
+ envelope.parentBeaconBlockRoot,
161
163
  envelope.executionRequests
162
164
  ),
163
165
 
@@ -219,23 +221,40 @@ export async function importExecutionPayload(
219
221
 
220
222
  // 6. Update fork choice, transitions the block's PENDING variant to FULL
221
223
  const execStatus = toForkChoiceExecutionStatus(execResult.status);
222
- this.forkChoice.onExecutionPayload(blockRootHex, blockHashHex, envelope.payload.blockNumber, execStatus);
224
+ this.forkChoice.onExecutionPayload(
225
+ blockRootHex,
226
+ blockHashHex,
227
+ envelope.payload.blockNumber,
228
+ execStatus,
229
+ dataAvailabilityStatus
230
+ );
231
+
232
+ // 7. Queue notifyForkchoiceUpdate to engine api
233
+ const head = this.forkChoice.getHead();
234
+ if (!this.opts.disableImportExecutionFcU && blockRootHex === head.blockRoot) {
235
+ const safeBlockHash = getSafeExecutionBlockHash(this.forkChoice);
236
+ const finalizedBlockHash = this.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
237
+ this.executionEngine.notifyForkchoiceUpdate(fork, blockHashHex, safeBlockHash, finalizedBlockHash).catch((e) => {
238
+ if (!isErrorAborted(e) && !isQueueErrorAborted(e)) {
239
+ this.logger.error("Error pushing notifyForkchoiceUpdate()", {blockHashHex, finalizedBlockHash}, e);
240
+ }
241
+ });
242
+ }
223
243
 
224
- // 7. Record metrics for payload envelope and column sources
244
+ // 8. Record metrics for payload envelope and column sources
225
245
  this.metrics?.importPayload.bySource.inc({source: payloadInput.getPayloadEnvelopeSource().source});
226
246
  for (const {source} of payloadInput.getSampledColumnsWithSource()) {
227
247
  this.metrics?.importPayload.columnsBySource.inc({source});
228
248
  }
229
249
 
230
- // 8. Emit event after payload is fully verified and imported to fork choice, only for recent enough payloads
250
+ // 9. Emit event after payload is fully verified and imported to fork choice, only for recent enough payloads
231
251
  if (this.clock.currentSlot - slot < EVENTSTREAM_EMIT_RECENT_EXECUTION_PAYLOAD_SLOTS) {
232
252
  this.emitter.emit(routes.events.EventType.executionPayload, {
233
253
  slot,
234
254
  builderIndex: envelope.builderIndex,
235
255
  blockHash: blockHashHex,
236
256
  blockRoot: blockRootHex,
237
- // TODO GLOAS: revisit once we support optimistic import
238
- executionOptimistic: false,
257
+ executionOptimistic: execStatus === ExecutionStatus.Syncing,
239
258
  });
240
259
  }
241
260
 
@@ -261,6 +280,6 @@ export async function processExecutionPayload(
261
280
  signal: AbortSignal,
262
281
  opts: ImportPayloadOpts = {}
263
282
  ): Promise<void> {
264
- await verifyPayloadsDataAvailability([payloadInput], signal);
265
- await importExecutionPayload.call(this, payloadInput, opts);
283
+ const {dataAvailabilityStatuses} = await verifyPayloadsDataAvailability([payloadInput], signal);
284
+ await importExecutionPayload.call(this, payloadInput, dataAvailabilityStatuses[0], opts);
266
285
  }
@@ -65,7 +65,7 @@ export async function processBlocks(
65
65
  }
66
66
 
67
67
  try {
68
- const {relevantBlocks, parentSlots, parentBlock} = verifyBlocksSanityChecks(this, blocks, opts);
68
+ const {relevantBlocks, parentSlots, parentBlock} = verifyBlocksSanityChecks(this, blocks, payloadEnvelopes, opts);
69
69
 
70
70
  // No relevant blocks, skip verifyBlocksInEpoch()
71
71
  if (relevantBlocks.length === 0 || parentBlock === null) {
@@ -90,8 +90,14 @@ export async function processBlocks(
90
90
 
91
91
  // Fully verify a block to be imported immediately after. Does not produce any side-effects besides adding intermediate
92
92
  // states in the state cache through regen.
93
- const {postStates, dataAvailabilityStatuses, proposerBalanceDeltas, segmentExecStatus, indexedAttestationsByBlock} =
94
- await verifyBlocksInEpoch.call(this, parentBlock, relevantBlocks, payloadEnvelopes, opts);
93
+ const {
94
+ postStates,
95
+ blockDAStatuses,
96
+ payloadDAStatuses,
97
+ proposerBalanceDeltas,
98
+ segmentExecStatus,
99
+ indexedAttestationsByBlock,
100
+ } = await verifyBlocksInEpoch.call(this, parentBlock, relevantBlocks, payloadEnvelopes, opts);
95
101
 
96
102
  // If segmentExecStatus has lvhForkchoice then, the entire segment should be invalid
97
103
  // and we need to further propagate
@@ -103,26 +109,37 @@ export async function processBlocks(
103
109
  }
104
110
 
105
111
  const {executionStatuses} = segmentExecStatus;
106
- const fullyVerifiedBlocks = relevantBlocks.map(
107
- (block, i): FullyVerifiedBlock => ({
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, {
108
116
  blockInput: block,
109
117
  postState: postStates[i],
110
118
  parentBlockSlot: parentSlots[i],
111
119
  executionStatus: executionStatuses[i],
112
120
  // start supporting optimistic syncing/processing
113
- dataAvailabilityStatus: dataAvailabilityStatuses[i],
121
+ dataAvailabilityStatus: blockDAStatuses[i],
114
122
  proposerBalanceDelta: proposerBalanceDeltas[i],
115
123
  indexedAttestations: indexedAttestationsByBlock[i],
116
124
  // TODO: Make this param mandatory and capture in gossip
117
125
  seenTimestampSec: opts.seenTimestampSec ?? Math.floor(Date.now() / 1000),
118
- })
119
- );
126
+ });
127
+ }
120
128
 
121
- for (const fullyVerifiedBlock of fullyVerifiedBlocks) {
122
- // TODO: Consider batching importBlock too if it takes significant time
123
- await importBlock.call(this, fullyVerifiedBlock, opts);
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
+ }
124
142
 
125
- const slot = fullyVerifiedBlock.blockInput.getBlock().message.slot;
126
143
  const payloadInput = payloadEnvelopes?.get(slot);
127
144
  if (payloadInput?.hasPayloadEnvelope()) {
128
145
  if (!payloadInput.isComplete()) {
@@ -130,9 +147,11 @@ export async function processBlocks(
130
147
  throw new Error(`Payload envelope for slot ${slot} not complete after DA verification`);
131
148
  }
132
149
  // 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});
150
+ const payloadDA = payloadDAStatuses.get(slot);
151
+ if (payloadDA === undefined) {
152
+ throw new Error(`Missing payload DA status for slot ${slot}`);
153
+ }
154
+ await importExecutionPayload.call(this, payloadInput, payloadDA, {validSignature: false});
136
155
  }
137
156
 
138
157
  await nextEventLoop();
@@ -64,6 +64,7 @@ export class PayloadEnvelopeInput {
64
64
  readonly proposerIndex: ValidatorIndex;
65
65
  readonly bid: gloas.ExecutionPayloadBid;
66
66
  readonly versionedHashes: VersionedHashes;
67
+ readonly daOutOfRange: boolean;
67
68
 
68
69
  private columnsCache = new Map<ColumnIndex, ColumnWithSource>();
69
70
 
@@ -87,6 +88,7 @@ export class PayloadEnvelopeInput {
87
88
  sampledColumns: ColumnIndex[];
88
89
  custodyColumns: ColumnIndex[];
89
90
  timeCreatedSec: number;
91
+ daOutOfRange: boolean;
90
92
  }) {
91
93
  this.blockRootHex = props.blockRootHex;
92
94
  this.slot = props.slot;
@@ -97,13 +99,14 @@ export class PayloadEnvelopeInput {
97
99
  this.sampledColumns = props.sampledColumns;
98
100
  this.custodyColumns = props.custodyColumns;
99
101
  this.timeCreatedSec = props.timeCreatedSec;
102
+ this.daOutOfRange = props.daOutOfRange;
100
103
  this.payloadEnvelopeDataPromise = createPromise();
101
104
  this.allDataPromise = createPromise();
102
105
  this.columnsDataPromise = createPromise();
103
106
 
104
107
  const noBlobs = props.bid.blobKzgCommitments.length === 0;
105
108
  const noSampledColumns = props.sampledColumns.length === 0;
106
- const hasAllData = noBlobs || noSampledColumns;
109
+ const hasAllData = props.daOutOfRange || noBlobs || noSampledColumns;
107
110
 
108
111
  if (hasAllData) {
109
112
  this.state = {hasPayload: false, hasAllData: true, hasComputedAllData: true};
@@ -125,6 +128,7 @@ export class PayloadEnvelopeInput {
125
128
  sampledColumns: props.sampledColumns,
126
129
  custodyColumns: props.custodyColumns,
127
130
  timeCreatedSec: props.timeCreatedSec,
131
+ daOutOfRange: props.daOutOfRange,
128
132
  });
129
133
  }
130
134
 
@@ -152,6 +156,7 @@ export class PayloadEnvelopeInput {
152
156
  throw new Error("Payload envelope beacon_block_root mismatch");
153
157
  }
154
158
 
159
+ // TODO GLOAS: track source by metrics, maybe inside the seen cache
155
160
  const source: SourceMeta = {
156
161
  source: props.source,
157
162
  seenTimestampSec: props.seenTimestampSec,
@@ -306,8 +311,11 @@ export class PayloadEnvelopeInput {
306
311
  return this.state.hasAllData;
307
312
  }
308
313
 
314
+ /**
315
+ * Strictly checks missing sampled columns. Does NOT short-circuit on `state.hasAllData`.
316
+ */
309
317
  getMissingSampledColumnMeta(): MissingColumnMeta {
310
- if (this.state.hasAllData) {
318
+ if (this.state.hasComputedAllData) {
311
319
  return {missing: [], versionedHashes: this.versionedHashes};
312
320
  }
313
321
 
@@ -27,6 +27,7 @@ export type CreateFromBlockProps = {
27
27
  sampledColumns: ColumnIndex[];
28
28
  custodyColumns: ColumnIndex[];
29
29
  timeCreatedSec: number;
30
+ daOutOfRange: boolean;
30
31
  };
31
32
 
32
33
  export type AddPayloadEnvelopeProps = SourceMeta & {
@@ -94,7 +94,8 @@ 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: PayloadSeparated (payload arrives separately as an envelope and is imported later)
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
98
99
  */
99
100
  export type FullyVerifiedBlock = {
100
101
  blockInput: IBlockInput;
@@ -41,6 +41,14 @@ 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
+ }
44
52
  // Track the execution hash before the last FULL advancement so we can recover
45
53
  // if the next block reveals that envelope was orphaned.
46
54
  let prevExecHash: string | null = currentExecHash;
@@ -41,7 +41,8 @@ export async function verifyBlocksInEpoch(
41
41
  postStates: IBeaconStateView[];
42
42
  proposerBalanceDeltas: number[];
43
43
  segmentExecStatus: SegmentExecStatus;
44
- dataAvailabilityStatuses: DataAvailabilityStatus[];
44
+ blockDAStatuses: DataAvailabilityStatus[];
45
+ payloadDAStatuses: Map<Slot, DataAvailabilityStatus>;
45
46
  indexedAttestationsByBlock: IndexedAttestation[][];
46
47
  }> {
47
48
  const blocks = blockInputs.map((blockInput) => blockInput.getBlock());
@@ -116,28 +117,52 @@ export async function verifyBlocksInEpoch(
116
117
 
117
118
  // Pick the data-availability source by fork:
118
119
  // - Pre-Gloas: blob/Fulu-column data lives in IBlockInput → verifyBlocksDataAvailability.
119
- // - Post-Gloas: verifyPayloadsDataAvailability
120
- const daAvailabilityPromise =
120
+ // - Post-Gloas: verifyPayloadsDataAvailability (payload-level DA, keyed by slot).
121
+ const daAvailabilityPromise: Promise<{
122
+ blockDAStatuses: DataAvailabilityStatus[];
123
+ payloadDAStatuses: Map<Slot, DataAvailabilityStatus>;
124
+ availableTime: number;
125
+ }> =
121
126
  fork >= ForkSeq.gloas
122
127
  ? (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);
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()) : [];
134
+ const {dataAvailabilityStatuses, availableTime} = await verifyPayloadsDataAvailability(
135
+ payloadInputsForDa,
136
+ abortController.signal
137
+ );
138
+
139
+ const payloadDAStatuses = new Map<Slot, DataAvailabilityStatus>();
140
+ for (let i = 0; i < payloadInputsForDa.length; i++) {
141
+ payloadDAStatuses.set(payloadInputsForDa[i].slot, dataAvailabilityStatuses[i]);
127
142
  }
128
- await verifyPayloadsDataAvailability(payloadInputsForDa, abortController.signal);
129
143
  return {
130
144
  // post-gloas, DataAvailabilityStatus is NotRequired for forkChoice.onBlock() ProtoBlock
131
- dataAvailabilityStatuses: blockInputs.map(() => DataAvailabilityStatus.NotRequired),
132
- availableTime: Date.now(),
145
+ blockDAStatuses: blockInputs.map(() => DataAvailabilityStatus.NotRequired),
146
+ payloadDAStatuses,
147
+ availableTime,
133
148
  };
134
149
  })()
135
- : verifyBlocksDataAvailability(blockInputs, abortController.signal);
150
+ : (async () => {
151
+ const {dataAvailabilityStatuses, availableTime} = await verifyBlocksDataAvailability(
152
+ blockInputs,
153
+ abortController.signal
154
+ );
155
+ return {
156
+ blockDAStatuses: dataAvailabilityStatuses,
157
+ payloadDAStatuses: new Map<Slot, DataAvailabilityStatus>(),
158
+ availableTime,
159
+ };
160
+ })();
136
161
 
137
162
  // batch all I/O operations to reduce overhead
138
163
  const [
139
164
  segmentExecStatus,
140
- {dataAvailabilityStatuses, availableTime},
165
+ {blockDAStatuses, payloadDAStatuses, availableTime},
141
166
  {postStates, proposerBalanceDeltas, verifyStateTime},
142
167
  {verifySignaturesTime},
143
168
  ] = await Promise.all([
@@ -258,7 +283,14 @@ export async function verifyBlocksInEpoch(
258
283
  );
259
284
  }
260
285
 
261
- return {postStates, dataAvailabilityStatuses, proposerBalanceDeltas, segmentExecStatus, indexedAttestationsByBlock};
286
+ return {
287
+ postStates,
288
+ blockDAStatuses,
289
+ payloadDAStatuses,
290
+ proposerBalanceDeltas,
291
+ segmentExecStatus,
292
+ indexedAttestationsByBlock,
293
+ };
262
294
  } finally {
263
295
  abortController.abort();
264
296
  }
@@ -46,8 +46,7 @@ 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}
50
- | {executionStatus: ExecutionStatus.PayloadSeparated; lvhResponse: undefined; execError: null};
49
+ | {executionStatus: ExecutionStatus.PreMerge; lvhResponse: undefined; execError: null};
51
50
 
52
51
  /**
53
52
  * Verifies 1 or more execution payloads from a linear sequence of blocks.
@@ -145,9 +144,10 @@ export async function verifyBlockExecutionPayload(
145
144
  ): Promise<VerifyBlockExecutionResponse> {
146
145
  const block = blockInput.getBlock();
147
146
 
148
- // Gloas block doesn't have execution payload. Return right away
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.
149
149
  if (isBlockInputNoData(blockInput)) {
150
- return {executionStatus: ExecutionStatus.PayloadSeparated, lvhResponse: undefined, execError: null};
150
+ return {executionStatus: ExecutionStatus.Syncing, lvhResponse: undefined, execError: null};
151
151
  }
152
152
 
153
153
  /** Not null if execution is enabled */
@@ -198,6 +198,7 @@ export async function verifyBlockExecutionPayload(
198
198
  executionStatus,
199
199
  latestValidExecHash: execResult.latestValidHash,
200
200
  invalidateFromParentBlockRoot: blockInput.parentRootHex,
201
+ invalidateFromParentBlockHash: toRootHex(executionPayloadEnabled.parentHash),
201
202
  };
202
203
  const execError = new BlockError(block, {
203
204
  code: BlockErrorCode.EXECUTION_ENGINE_ERROR,
@@ -281,6 +282,7 @@ function getSegmentErrorResponse(
281
282
  executionStatus: ExecutionStatus.Invalid,
282
283
  latestValidExecHash: lvhResponse.latestValidExecHash,
283
284
  invalidateFromParentBlockRoot: parentBlock.blockRoot,
285
+ invalidateFromParentBlockHash: parentBlock.executionPayloadBlockHash,
284
286
  };
285
287
  }
286
288
  }
@@ -7,6 +7,7 @@ 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";
10
11
  import {ImportBlockOpts} from "./types.js";
11
12
 
12
13
  /**
@@ -30,6 +31,7 @@ export function verifyBlocksSanityChecks(
30
31
  blacklistedBlocks: Map<RootHex, Slot | null>;
31
32
  },
32
33
  blocks: IBlockInput[],
34
+ payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
33
35
  opts: ImportBlockOpts
34
36
  ): {
35
37
  relevantBlocks: IBlockInput[];
@@ -100,13 +102,21 @@ export function verifyBlocksSanityChecks(
100
102
  const parentBlockHash = toRootHex(block.message.body.signedExecutionPayloadBid.message.parentBlockHash);
101
103
  const parentBlockWithPayload = chain.forkChoice.getBlockHexAndBlockHash(parentRoot, parentBlockHash);
102
104
  if (!parentBlockWithPayload) {
103
- throw new BlockError(block, {
104
- code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN,
105
- parentRoot,
106
- parentBlockHash,
107
- });
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;
108
119
  }
109
- parentBlock = parentBlockWithPayload;
110
120
  }
111
121
  // Parent is known to the fork-choice
112
122
  parentBlockSlot = parentBlock.slot;
@@ -20,7 +20,7 @@ export type VerifyExecutionPayloadEnvelopeOpts = {
20
20
  * performed outside this function, see `verifyExecutionPayloadEnvelopeSignature` and
21
21
  * `importExecutionPayload` which run both in parallel with this check.
22
22
  *
23
- * Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.5/specs/gloas/fork-choice.md#new-verify_execution_payload_envelope
23
+ * Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.6/specs/gloas/fork-choice.md#new-verify_execution_payload_envelope
24
24
  */
25
25
  export function verifyExecutionPayloadEnvelope(
26
26
  config: BeaconConfig,
@@ -43,6 +43,11 @@ export function verifyExecutionPayloadEnvelope(
43
43
  `Envelope's block is not the latest block header envelope=${toRootHex(envelope.beaconBlockRoot)} latestBlockHeader=${toRootHex(headerRoot)}`
44
44
  );
45
45
  }
46
+ if (!byteArrayEquals(envelope.parentBeaconBlockRoot, state.latestBlockHeader.parentRoot)) {
47
+ throw new Error(
48
+ `Envelope's parent_beacon_block_root mismatch envelope=${toRootHex(envelope.parentBeaconBlockRoot)} state=${toRootHex(state.latestBlockHeader.parentRoot)}`
49
+ );
50
+ }
46
51
 
47
52
  // Verify consistency with the committed bid
48
53
  const bid = state.latestExecutionPayloadBid;
@@ -108,7 +113,7 @@ export function verifyExecutionPayloadEnvelope(
108
113
  /**
109
114
  * Verify the BLS signature of an execution payload envelope.
110
115
  *
111
- * Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.5/specs/gloas/fork-choice.md#new-verify_execution_payload_envelope_signature
116
+ * Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.6/specs/gloas/fork-choice.md#new-verify_execution_payload_envelope_signature
112
117
  */
113
118
  export async function verifyExecutionPayloadEnvelopeSignature(
114
119
  config: BeaconConfig,
@@ -28,11 +28,14 @@ export async function verifyPayloadsDataAvailability(
28
28
  await Promise.all(promises);
29
29
 
30
30
  const availableTime = Math.max(0, Math.max(...payloadInputs.map((payloadInput) => payloadInput.getTimeComplete())));
31
- const dataAvailabilityStatuses: DataAvailabilityStatus[] = payloadInputs.map((payloadInput) =>
32
- payloadInput.getBlobKzgCommitments().length === 0
31
+ const dataAvailabilityStatuses: DataAvailabilityStatus[] = payloadInputs.map((payloadInput) => {
32
+ if (payloadInput.daOutOfRange) {
33
+ return DataAvailabilityStatus.OutOfRange;
34
+ }
35
+ return payloadInput.getBlobKzgCommitments().length === 0
33
36
  ? DataAvailabilityStatus.NotRequired
34
- : DataAvailabilityStatus.Available
35
- );
37
+ : DataAvailabilityStatus.Available;
38
+ });
36
39
 
37
40
  return {dataAvailabilityStatuses, availableTime};
38
41
  }