@lodestar/beacon-node 1.43.0-dev.2870b59b6a → 1.43.0-dev.3d6ae250a4

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 (197) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +1 -4
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/state/utils.d.ts +2 -2
  5. package/lib/api/impl/beacon/state/utils.d.ts.map +1 -1
  6. package/lib/api/impl/beacon/state/utils.js.map +1 -1
  7. package/lib/api/impl/validator/index.d.ts.map +1 -1
  8. package/lib/api/impl/validator/index.js +1 -4
  9. package/lib/api/impl/validator/index.js.map +1 -1
  10. package/lib/chain/GetBlobsTracker.d.ts +1 -1
  11. package/lib/chain/GetBlobsTracker.d.ts.map +1 -1
  12. package/lib/chain/GetBlobsTracker.js +1 -2
  13. package/lib/chain/GetBlobsTracker.js.map +1 -1
  14. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  15. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  16. package/lib/chain/archiveStore/interface.d.ts +4 -4
  17. package/lib/chain/archiveStore/interface.d.ts.map +1 -1
  18. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts +4 -4
  19. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts.map +1 -1
  20. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js +2 -4
  21. package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js.map +1 -1
  22. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts +2 -2
  23. package/lib/chain/archiveStore/utils/archiveBlocks.d.ts.map +1 -1
  24. package/lib/chain/archiveStore/utils/archiveBlocks.js +110 -58
  25. package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
  26. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  27. package/lib/chain/blocks/importBlock.js +23 -31
  28. package/lib/chain/blocks/importBlock.js.map +1 -1
  29. package/lib/chain/blocks/importExecutionPayload.d.ts +15 -14
  30. package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
  31. package/lib/chain/blocks/importExecutionPayload.js +63 -85
  32. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  33. package/lib/chain/blocks/index.d.ts.map +1 -1
  34. package/lib/chain/blocks/index.js +1 -2
  35. package/lib/chain/blocks/index.js.map +1 -1
  36. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +3 -0
  37. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
  38. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +20 -0
  39. package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
  40. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts +5 -0
  41. package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts.map +1 -1
  42. package/lib/chain/blocks/payloadEnvelopeProcessor.js +6 -4
  43. package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
  44. package/lib/chain/blocks/types.d.ts +15 -21
  45. package/lib/chain/blocks/types.d.ts.map +1 -1
  46. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +24 -0
  47. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -0
  48. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +76 -0
  49. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -0
  50. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts +14 -0
  51. package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -0
  52. package/lib/chain/blocks/verifyPayloadsDataAvailability.js +25 -0
  53. package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -0
  54. package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts +1 -1
  55. package/lib/chain/blocks/writePayloadEnvelopeInputToDb.d.ts.map +1 -1
  56. package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js +2 -11
  57. package/lib/chain/blocks/writePayloadEnvelopeInputToDb.js.map +1 -1
  58. package/lib/chain/chain.d.ts +5 -4
  59. package/lib/chain/chain.d.ts.map +1 -1
  60. package/lib/chain/chain.js +23 -32
  61. package/lib/chain/chain.js.map +1 -1
  62. package/lib/chain/emitter.d.ts +16 -4
  63. package/lib/chain/emitter.d.ts.map +1 -1
  64. package/lib/chain/emitter.js +5 -0
  65. package/lib/chain/emitter.js.map +1 -1
  66. package/lib/chain/errors/executionPayloadBid.d.ts +5 -0
  67. package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
  68. package/lib/chain/errors/executionPayloadBid.js +1 -0
  69. package/lib/chain/errors/executionPayloadBid.js.map +1 -1
  70. package/lib/chain/errors/executionPayloadEnvelope.d.ts +5 -0
  71. package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
  72. package/lib/chain/errors/executionPayloadEnvelope.js +1 -0
  73. package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
  74. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  75. package/lib/chain/forkChoice/index.js +11 -15
  76. package/lib/chain/forkChoice/index.js.map +1 -1
  77. package/lib/chain/interface.d.ts +4 -3
  78. package/lib/chain/interface.d.ts.map +1 -1
  79. package/lib/chain/interface.js.map +1 -1
  80. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  81. package/lib/chain/prepareNextSlot.js +48 -22
  82. package/lib/chain/prepareNextSlot.js.map +1 -1
  83. package/lib/chain/produceBlock/computeNewStateRoot.d.ts +3 -9
  84. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  85. package/lib/chain/produceBlock/computeNewStateRoot.js +5 -32
  86. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  87. package/lib/chain/produceBlock/produceBlockBody.d.ts +4 -8
  88. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  89. package/lib/chain/produceBlock/produceBlockBody.js +48 -23
  90. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  91. package/lib/chain/regen/errors.d.ts +1 -11
  92. package/lib/chain/regen/errors.d.ts.map +1 -1
  93. package/lib/chain/regen/errors.js +0 -2
  94. package/lib/chain/regen/errors.js.map +1 -1
  95. package/lib/chain/regen/interface.d.ts +6 -11
  96. package/lib/chain/regen/interface.d.ts.map +1 -1
  97. package/lib/chain/regen/queued.d.ts +6 -10
  98. package/lib/chain/regen/queued.d.ts.map +1 -1
  99. package/lib/chain/regen/queued.js +3 -10
  100. package/lib/chain/regen/queued.js.map +1 -1
  101. package/lib/chain/regen/regen.d.ts +0 -5
  102. package/lib/chain/regen/regen.d.ts.map +1 -1
  103. package/lib/chain/regen/regen.js +0 -8
  104. package/lib/chain/regen/regen.js.map +1 -1
  105. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +11 -4
  106. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  107. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +20 -18
  108. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  109. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +1 -7
  110. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  111. package/lib/chain/stateCache/persistentCheckpointsCache.js +4 -9
  112. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  113. package/lib/chain/stateCache/types.d.ts +0 -6
  114. package/lib/chain/stateCache/types.d.ts.map +1 -1
  115. package/lib/chain/stateCache/types.js.map +1 -1
  116. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  117. package/lib/chain/validation/executionPayloadBid.js +13 -1
  118. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  119. package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
  120. package/lib/chain/validation/executionPayloadEnvelope.js +20 -10
  121. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  122. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  123. package/lib/chain/validation/payloadAttestationMessage.js +4 -3
  124. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  125. package/lib/db/repositories/executionPayloadEnvelopeArchive.js +1 -1
  126. package/lib/db/repositories/executionPayloadEnvelopeArchive.js.map +1 -1
  127. package/lib/execution/engine/http.d.ts.map +1 -1
  128. package/lib/execution/engine/http.js +21 -14
  129. package/lib/execution/engine/http.js.map +1 -1
  130. package/lib/execution/engine/interface.d.ts +1 -0
  131. package/lib/execution/engine/interface.d.ts.map +1 -1
  132. package/lib/execution/engine/mock.d.ts.map +1 -1
  133. package/lib/execution/engine/mock.js +6 -0
  134. package/lib/execution/engine/mock.js.map +1 -1
  135. package/lib/execution/engine/types.d.ts +20 -0
  136. package/lib/execution/engine/types.d.ts.map +1 -1
  137. package/lib/execution/engine/types.js +18 -0
  138. package/lib/execution/engine/types.js.map +1 -1
  139. package/lib/network/gossip/topic.d.ts +750 -2
  140. package/lib/network/gossip/topic.d.ts.map +1 -1
  141. package/lib/network/network.js +1 -1
  142. package/lib/network/network.js.map +1 -1
  143. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  144. package/lib/network/processor/gossipHandlers.js +22 -6
  145. package/lib/network/processor/gossipHandlers.js.map +1 -1
  146. package/lib/node/nodejs.d.ts.map +1 -1
  147. package/lib/node/nodejs.js +4 -2
  148. package/lib/node/nodejs.js.map +1 -1
  149. package/lib/util/sszBytes.d.ts.map +1 -1
  150. package/lib/util/sszBytes.js +16 -3
  151. package/lib/util/sszBytes.js.map +1 -1
  152. package/package.json +17 -16
  153. package/src/api/impl/beacon/blocks/index.ts +1 -4
  154. package/src/api/impl/beacon/state/utils.ts +2 -2
  155. package/src/api/impl/validator/index.ts +3 -6
  156. package/src/chain/GetBlobsTracker.ts +1 -2
  157. package/src/chain/archiveStore/archiveStore.ts +5 -5
  158. package/src/chain/archiveStore/interface.ts +4 -4
  159. package/src/chain/archiveStore/strategies/frequencyStateArchiveStrategy.ts +6 -8
  160. package/src/chain/archiveStore/utils/archiveBlocks.ts +153 -94
  161. package/src/chain/blocks/importBlock.ts +22 -35
  162. package/src/chain/blocks/importExecutionPayload.ts +77 -103
  163. package/src/chain/blocks/index.ts +1 -2
  164. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +27 -0
  165. package/src/chain/blocks/payloadEnvelopeProcessor.ts +6 -5
  166. package/src/chain/blocks/types.ts +15 -26
  167. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +129 -0
  168. package/src/chain/blocks/verifyPayloadsDataAvailability.ts +38 -0
  169. package/src/chain/blocks/writePayloadEnvelopeInputToDb.ts +9 -18
  170. package/src/chain/chain.ts +35 -48
  171. package/src/chain/emitter.ts +15 -3
  172. package/src/chain/errors/executionPayloadBid.ts +6 -0
  173. package/src/chain/errors/executionPayloadEnvelope.ts +6 -0
  174. package/src/chain/forkChoice/index.ts +8 -20
  175. package/src/chain/interface.ts +4 -2
  176. package/src/chain/prepareNextSlot.ts +62 -23
  177. package/src/chain/produceBlock/computeNewStateRoot.ts +6 -43
  178. package/src/chain/produceBlock/produceBlockBody.ts +66 -26
  179. package/src/chain/regen/errors.ts +1 -6
  180. package/src/chain/regen/interface.ts +6 -11
  181. package/src/chain/regen/queued.ts +6 -14
  182. package/src/chain/regen/regen.ts +0 -8
  183. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +22 -20
  184. package/src/chain/stateCache/persistentCheckpointsCache.ts +5 -15
  185. package/src/chain/stateCache/types.ts +0 -3
  186. package/src/chain/validation/executionPayloadBid.ts +14 -0
  187. package/src/chain/validation/executionPayloadEnvelope.ts +21 -11
  188. package/src/chain/validation/payloadAttestationMessage.ts +5 -3
  189. package/src/db/repositories/executionPayloadEnvelopeArchive.ts +1 -1
  190. package/src/execution/engine/http.ts +21 -14
  191. package/src/execution/engine/interface.ts +1 -0
  192. package/src/execution/engine/mock.ts +8 -1
  193. package/src/execution/engine/types.ts +41 -0
  194. package/src/network/network.ts +1 -1
  195. package/src/network/processor/gossipHandlers.ts +26 -10
  196. package/src/node/nodejs.ts +4 -2
  197. package/src/util/sszBytes.ts +21 -3
@@ -2,10 +2,9 @@ import path from "node:path";
2
2
  import {PrivateKey} from "@libp2p/interface";
3
3
  import {Type} from "@chainsafe/ssz";
4
4
  import {BeaconConfig} from "@lodestar/config";
5
- import {CheckpointWithPayloadStatus, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice";
5
+ import {CheckpointWithHex, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice";
6
6
  import {LoggerNode} from "@lodestar/logger/node";
7
7
  import {
8
- BUILDER_INDEX_SELF_BUILD,
9
8
  EFFECTIVE_BALANCE_INCREMENT,
10
9
  type ForkPostFulu,
11
10
  type ForkPostGloas,
@@ -24,7 +23,6 @@ import {
24
23
  getEffectiveBalancesFromStateBytes,
25
24
  isStatePostAltair,
26
25
  isStatePostElectra,
27
- isStatePostGloas,
28
26
  } from "@lodestar/state-transition";
29
27
  import {
30
28
  BeaconBlock,
@@ -41,6 +39,7 @@ import {
41
39
  ValidatorIndex,
42
40
  Wei,
43
41
  deneb,
42
+ electra,
44
43
  gloas,
45
44
  isBlindedBeaconBlock,
46
45
  phase0,
@@ -93,8 +92,8 @@ import {
93
92
  } from "./opPools/index.js";
94
93
  import {IChainOptions} from "./options.js";
95
94
  import {PrepareNextSlotScheduler} from "./prepareNextSlot.js";
96
- import {computeNewStateRoot, computePayloadEnvelopeStateRoot} from "./produceBlock/computeNewStateRoot.js";
97
- import {AssembledBlockType, BlockType, ProduceFullGloas, ProduceResult} from "./produceBlock/index.js";
95
+ import {computeNewStateRoot} from "./produceBlock/computeNewStateRoot.js";
96
+ import {AssembledBlockType, BlockType, ProduceResult} from "./produceBlock/index.js";
98
97
  import {BlockAttributes, produceBlockBody, produceCommonBlockBody} from "./produceBlock/produceBlockBody.js";
99
98
  import {QueuedStateRegenerator, RegenCaller} from "./regen/index.js";
100
99
  import {ReprocessController} from "./reprocess.js";
@@ -118,7 +117,7 @@ import {DbCPStateDatastore, checkpointToDatastoreKey} from "./stateCache/datasto
118
117
  import {FileCPStateDatastore} from "./stateCache/datastore/file.js";
119
118
  import {CPStateDatastore} from "./stateCache/datastore/types.js";
120
119
  import {FIFOBlockStateCache} from "./stateCache/fifoBlockStateCache.js";
121
- import {PersistentCheckpointStateCache, fcCheckpointToHexPayload} from "./stateCache/persistentCheckpointsCache.js";
120
+ import {PersistentCheckpointStateCache} from "./stateCache/persistentCheckpointsCache.js";
122
121
  import {CheckpointStateCache} from "./stateCache/types.js";
123
122
  import {ValidatorMonitor} from "./validatorMonitor.js";
124
123
 
@@ -682,11 +681,11 @@ export class BeaconChain implements IBeaconChain {
682
681
  }
683
682
 
684
683
  getStateByCheckpoint(
685
- checkpoint: CheckpointWithPayloadStatus
684
+ checkpoint: CheckpointWithHex
686
685
  ): {state: IBeaconStateView; executionOptimistic: boolean; finalized: boolean} | null {
687
686
  // finalized or justified checkpoint states maynot be available with PersistentCheckpointStateCache, use getCheckpointStateOrBytes() api to get Uint8Array
688
- const checkpointHexPayload = fcCheckpointToHexPayload(checkpoint);
689
- const cachedStateCtx = this.regen.getCheckpointStateSync(checkpointHexPayload);
687
+ const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex};
688
+ const cachedStateCtx = this.regen.getCheckpointStateSync(checkpointHex);
690
689
  if (cachedStateCtx) {
691
690
  const block = this.forkChoice.getBlockDefaultStatus(
692
691
  ssz.phase0.BeaconBlockHeader.hashTreeRoot(cachedStateCtx.latestBlockHeader)
@@ -703,10 +702,10 @@ export class BeaconChain implements IBeaconChain {
703
702
  }
704
703
 
705
704
  async getStateOrBytesByCheckpoint(
706
- checkpoint: CheckpointWithPayloadStatus
705
+ checkpoint: CheckpointWithHex
707
706
  ): Promise<{state: IBeaconStateView | Uint8Array; executionOptimistic: boolean; finalized: boolean} | null> {
708
- const checkpointHexPayload = fcCheckpointToHexPayload(checkpoint);
709
- const cachedStateCtx = await this.regen.getCheckpointStateOrBytes(checkpointHexPayload);
707
+ const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex};
708
+ const cachedStateCtx = await this.regen.getCheckpointStateOrBytes(checkpointHex);
710
709
  if (cachedStateCtx) {
711
710
  const block = this.forkChoice.getBlockDefaultStatus(checkpoint.root);
712
711
  const finalizedEpoch = this.forkChoice.getFinalizedCheckpoint().epoch;
@@ -888,6 +887,17 @@ export class BeaconChain implements IBeaconChain {
888
887
  );
889
888
  }
890
889
 
890
+ async getParentExecutionRequests(
891
+ parentBlockSlot: Slot,
892
+ parentBlockRootHex: RootHex
893
+ ): Promise<electra.ExecutionRequests> {
894
+ const envelope = await this.getExecutionPayloadEnvelope(parentBlockSlot, parentBlockRootHex);
895
+ if (envelope === null) {
896
+ throw Error(`Parent execution payload envelope not found slot=${parentBlockSlot}, root=${parentBlockRootHex}`);
897
+ }
898
+ return envelope.message.executionRequests;
899
+ }
900
+
891
901
  async getDataColumnSidecars(blockSlot: Slot, blockRootHex: string): Promise<DataColumnSidecar[]> {
892
902
  const fork = this.config.getForkName(blockSlot);
893
903
 
@@ -1062,7 +1072,7 @@ export class BeaconChain implements IBeaconChain {
1062
1072
  body,
1063
1073
  } as AssembledBlockType<T>;
1064
1074
 
1065
- const {newStateRoot, proposerReward, postBlockState} = computeNewStateRoot(this.metrics, state, block);
1075
+ const {newStateRoot, proposerReward} = computeNewStateRoot(this.metrics, state, block);
1066
1076
  block.stateRoot = newStateRoot;
1067
1077
  const blockRoot =
1068
1078
  produceResult.type === BlockType.Full
@@ -1071,26 +1081,9 @@ export class BeaconChain implements IBeaconChain {
1071
1081
  const blockRootHex = toRootHex(blockRoot);
1072
1082
 
1073
1083
  const fork = this.config.getForkName(slot);
1074
- if (isForkPostGloas(fork)) {
1075
- // TODO GLOAS: we should retire BlockType post-gloas, may need a new enum for self vs non-self built
1076
- if (produceResult.type !== BlockType.Full) {
1077
- throw Error(`Unexpected block type=${produceResult.type} for post-gloas fork=${fork}`);
1078
- }
1079
-
1080
- const gloasResult = produceResult as ProduceFullGloas;
1081
- const envelope: gloas.ExecutionPayloadEnvelope = {
1082
- payload: gloasResult.executionPayload,
1083
- executionRequests: gloasResult.executionRequests,
1084
- builderIndex: BUILDER_INDEX_SELF_BUILD,
1085
- beaconBlockRoot: blockRoot,
1086
- slot,
1087
- stateRoot: ZERO_HASH,
1088
- };
1089
- if (!isStatePostGloas(postBlockState)) {
1090
- throw Error(`Expected gloas+ post-state for execution payload envelope, got fork=${postBlockState.forkName}`);
1091
- }
1092
- const payloadEnvelopeStateRoot = computePayloadEnvelopeStateRoot(this.metrics, postBlockState, envelope);
1093
- gloasResult.payloadEnvelopeStateRoot = payloadEnvelopeStateRoot;
1084
+ // TODO GLOAS: we should retire BlockType post-gloas, may need a new enum for self vs non-self built
1085
+ if (isForkPostGloas(fork) && produceResult.type !== BlockType.Full) {
1086
+ throw Error(`Unexpected block type=${produceResult.type} for post-gloas fork=${fork}`);
1094
1087
  }
1095
1088
 
1096
1089
  // Track the produced block for consensus broadcast validations, later validation, etc.
@@ -1296,7 +1289,7 @@ export class BeaconChain implements IBeaconChain {
1296
1289
  * @param blockState state that declares justified checkpoint `checkpoint`
1297
1290
  */
1298
1291
  private justifiedBalancesGetter(
1299
- checkpoint: CheckpointWithPayloadStatus,
1292
+ checkpoint: CheckpointWithHex,
1300
1293
  blockState: IBeaconStateView
1301
1294
  ): EffectiveBalanceIncrements {
1302
1295
  this.metrics?.balancesCache.requests.inc();
@@ -1335,11 +1328,11 @@ export class BeaconChain implements IBeaconChain {
1335
1328
  * @param blockState state that declares justified checkpoint `checkpoint`
1336
1329
  */
1337
1330
  private closestJustifiedBalancesStateToCheckpoint(
1338
- checkpoint: CheckpointWithPayloadStatus,
1331
+ checkpoint: CheckpointWithHex,
1339
1332
  blockState: IBeaconStateView
1340
1333
  ): {state: IBeaconStateView; stateId: string; shouldWarn: boolean} {
1341
- const checkpointHexPayload = fcCheckpointToHexPayload(checkpoint);
1342
- const state = this.regen.getCheckpointStateSync(checkpointHexPayload);
1334
+ const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex};
1335
+ const state = this.regen.getCheckpointStateSync(checkpointHex);
1343
1336
  if (state) {
1344
1337
  return {state, stateId: "checkpoint_state", shouldWarn: false};
1345
1338
  }
@@ -1350,10 +1343,7 @@ export class BeaconChain implements IBeaconChain {
1350
1343
  }
1351
1344
 
1352
1345
  // Find a state in the same branch of checkpoint at same epoch. Balances should exactly the same
1353
- for (const descendantBlock of this.forkChoice.forwardIterateDescendants(
1354
- checkpoint.rootHex,
1355
- checkpoint.payloadStatus
1356
- )) {
1346
+ for (const descendantBlock of this.forkChoice.forwardIterateDescendantsDefaultStatus(checkpoint.rootHex)) {
1357
1347
  if (computeEpochAtSlot(descendantBlock.slot) === checkpoint.epoch) {
1358
1348
  const descendantBlockState = this.regen.getStateSync(descendantBlock.stateRoot);
1359
1349
  if (descendantBlockState) {
@@ -1369,10 +1359,7 @@ export class BeaconChain implements IBeaconChain {
1369
1359
 
1370
1360
  // Find a state in the same branch of checkpoint at a latter epoch. Balances are not the same, but should be close
1371
1361
  // Note: must call .forwardIterateDescendants() again since nodes are not sorted
1372
- for (const descendantBlock of this.forkChoice.forwardIterateDescendants(
1373
- checkpoint.rootHex,
1374
- checkpoint.payloadStatus
1375
- )) {
1362
+ for (const descendantBlock of this.forkChoice.forwardIterateDescendantsDefaultStatus(checkpoint.rootHex)) {
1376
1363
  if (computeEpochAtSlot(descendantBlock.slot) > checkpoint.epoch) {
1377
1364
  const descendantBlockState = this.regen.getStateSync(descendantBlock.stateRoot);
1378
1365
  if (descendantBlockState) {
@@ -1476,7 +1463,7 @@ export class BeaconChain implements IBeaconChain {
1476
1463
  this.seenContributionAndProof.prune(head.slot);
1477
1464
  }
1478
1465
 
1479
- private onForkChoiceJustified(this: BeaconChain, cp: CheckpointWithPayloadStatus): void {
1466
+ private onForkChoiceJustified(this: BeaconChain, cp: CheckpointWithHex): void {
1480
1467
  this.logger.verbose("Fork choice justified", {epoch: cp.epoch, root: cp.rootHex});
1481
1468
  }
1482
1469
 
@@ -1487,7 +1474,7 @@ export class BeaconChain implements IBeaconChain {
1487
1474
  });
1488
1475
  }
1489
1476
 
1490
- private async onForkChoiceFinalized(this: BeaconChain, cp: CheckpointWithPayloadStatus): Promise<void> {
1477
+ private async onForkChoiceFinalized(this: BeaconChain, cp: CheckpointWithHex): Promise<void> {
1491
1478
  this.logger.verbose("Fork choice finalized", {epoch: cp.epoch, root: cp.rootHex});
1492
1479
  const finalizedSlot = computeStartSlotAtEpoch(cp.epoch);
1493
1480
  this.seenBlockProposers.prune(finalizedSlot);
@@ -1528,7 +1515,7 @@ export class BeaconChain implements IBeaconChain {
1528
1515
  }
1529
1516
  }
1530
1517
 
1531
- private async updateValidatorsCustodyRequirement(finalizedCheckpoint: CheckpointWithPayloadStatus): Promise<void> {
1518
+ private async updateValidatorsCustodyRequirement(finalizedCheckpoint: CheckpointWithHex): Promise<void> {
1532
1519
  if (this.custodyConfig.targetCustodyGroupCount === this.config.NUMBER_OF_CUSTODY_GROUPS) {
1533
1520
  // Custody requirements can only be increased, we can disable dynamic custody updates
1534
1521
  // if the node already maintains custody of all custody groups in case it is configured
@@ -1,12 +1,13 @@
1
1
  import {EventEmitter} from "node:events";
2
2
  import {StrictEventEmitter} from "strict-event-emitter-types";
3
3
  import {routes} from "@lodestar/api";
4
- import {CheckpointWithPayloadStatus} from "@lodestar/fork-choice";
4
+ import {CheckpointWithHex} from "@lodestar/fork-choice";
5
5
  import {IBeaconStateView} from "@lodestar/state-transition";
6
6
  import {DataColumnSidecar, RootHex, deneb, phase0} from "@lodestar/types";
7
7
  import {SignedExecutionPayloadEnvelope} from "@lodestar/types/gloas";
8
8
  import {PeerIdStr} from "../util/peerId.js";
9
9
  import {BlockInputSource, IBlockInput} from "./blocks/blockInput/types.js";
10
+ import {PayloadEnvelopeInput} from "./blocks/payloadEnvelopeInput/payloadEnvelopeInput.js";
10
11
 
11
12
  /**
12
13
  * Important chain events that occur during normal chain operation.
@@ -76,6 +77,11 @@ export enum ChainEvent {
76
77
  * cut-off window passes for waiting on gossip
77
78
  */
78
79
  incompleteBlockInput = "incompleteBlockInput",
80
+ /**
81
+ * Post-gloas: trigger BlockInputSync for payload envelopes whose envelope and/or sampled columns are partially
82
+ * received via gossip but are not complete by time the cut-off window passes for waiting on gossip
83
+ */
84
+ incompletePayloadEnvelope = "incompletePayloadEnvelope",
79
85
  }
80
86
 
81
87
  export type HeadEventData = routes.events.EventData[routes.events.EventType.head];
@@ -93,14 +99,19 @@ export type ChainEventData = {
93
99
  };
94
100
  [ChainEvent.unknownBlockRoot]: {rootHex: RootHex; peer?: PeerIdStr; source: BlockInputSource};
95
101
  [ChainEvent.incompleteBlockInput]: {blockInput: IBlockInput; peer: PeerIdStr; source: BlockInputSource};
102
+ [ChainEvent.incompletePayloadEnvelope]: {
103
+ payloadInput: PayloadEnvelopeInput;
104
+ peer: PeerIdStr;
105
+ source: BlockInputSource;
106
+ };
96
107
  [ChainEvent.unknownEnvelopeBlockRoot]: {rootHex: RootHex; peer?: PeerIdStr; source: BlockInputSource};
97
108
  };
98
109
 
99
110
  export type IChainEvents = ApiEvents & {
100
111
  [ChainEvent.checkpoint]: (checkpoint: phase0.Checkpoint, state: IBeaconStateView) => void;
101
112
 
102
- [ChainEvent.forkChoiceJustified]: (checkpoint: CheckpointWithPayloadStatus) => void;
103
- [ChainEvent.forkChoiceFinalized]: (checkpoint: CheckpointWithPayloadStatus) => void;
113
+ [ChainEvent.forkChoiceJustified]: (checkpoint: CheckpointWithHex) => void;
114
+ [ChainEvent.forkChoiceFinalized]: (checkpoint: CheckpointWithHex) => void;
104
115
 
105
116
  [ChainEvent.updateTargetCustodyGroupCount]: (targetGroupCount: number) => void;
106
117
 
@@ -116,6 +127,7 @@ export type IChainEvents = ApiEvents & {
116
127
  [ChainEvent.envelopeUnknownBlock]: (data: ChainEventData[ChainEvent.envelopeUnknownBlock]) => void;
117
128
  [ChainEvent.unknownBlockRoot]: (data: ChainEventData[ChainEvent.unknownBlockRoot]) => void;
118
129
  [ChainEvent.incompleteBlockInput]: (data: ChainEventData[ChainEvent.incompleteBlockInput]) => void;
130
+ [ChainEvent.incompletePayloadEnvelope]: (data: ChainEventData[ChainEvent.incompletePayloadEnvelope]) => void;
119
131
  [ChainEvent.unknownEnvelopeBlockRoot]: (data: ChainEventData[ChainEvent.unknownEnvelopeBlockRoot]) => void;
120
132
  };
121
133
 
@@ -7,6 +7,7 @@ export enum ExecutionPayloadBidErrorCode {
7
7
  BID_ALREADY_KNOWN = "EXECUTION_PAYLOAD_BID_ERROR_BID_ALREADY_KNOWN",
8
8
  BID_TOO_LOW = "EXECUTION_PAYLOAD_BID_ERROR_BID_TOO_LOW",
9
9
  BID_TOO_HIGH = "EXECUTION_PAYLOAD_BID_ERROR_BID_TOO_HIGH",
10
+ TOO_MANY_KZG_COMMITMENTS = "EXECUTION_PAYLOAD_BID_ERROR_TOO_MANY_KZG_COMMITMENTS",
10
11
  UNKNOWN_BLOCK_ROOT = "EXECUTION_PAYLOAD_BID_ERROR_UNKNOWN_BLOCK_ROOT",
11
12
  INVALID_SLOT = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SLOT",
12
13
  INVALID_SIGNATURE = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SIGNATURE",
@@ -28,6 +29,11 @@ export type ExecutionPayloadBidErrorType =
28
29
  }
29
30
  | {code: ExecutionPayloadBidErrorCode.BID_TOO_LOW; bidValue: number; currentHighestBid: number}
30
31
  | {code: ExecutionPayloadBidErrorCode.BID_TOO_HIGH; bidValue: number; builderBalance: number}
32
+ | {
33
+ code: ExecutionPayloadBidErrorCode.TOO_MANY_KZG_COMMITMENTS;
34
+ blobKzgCommitmentsLen: number;
35
+ commitmentLimit: number;
36
+ }
31
37
  | {code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT; parentBlockRoot: RootHex}
32
38
  | {code: ExecutionPayloadBidErrorCode.INVALID_SLOT; builderIndex: BuilderIndex; slot: Slot}
33
39
  | {code: ExecutionPayloadBidErrorCode.INVALID_SIGNATURE; builderIndex: BuilderIndex; slot: Slot};
@@ -11,6 +11,7 @@ export enum ExecutionPayloadEnvelopeErrorCode {
11
11
  SLOT_MISMATCH = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_SLOT_MISMATCH",
12
12
  BUILDER_INDEX_MISMATCH = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_BUILDER_INDEX_MISMATCH",
13
13
  BLOCK_HASH_MISMATCH = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_BLOCK_HASH_MISMATCH",
14
+ EXECUTION_REQUESTS_ROOT_MISMATCH = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_EXECUTION_REQUESTS_ROOT_MISMATCH",
14
15
  INVALID_SIGNATURE = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_INVALID_SIGNATURE",
15
16
  PAYLOAD_ENVELOPE_INPUT_MISSING = "EXECUTION_PAYLOAD_ENVELOPE_ERROR_PAYLOAD_ENVELOPE_INPUT_MISSING",
16
17
  }
@@ -36,6 +37,11 @@ export type ExecutionPayloadEnvelopeErrorType =
36
37
  envelopeBlockHash: RootHex;
37
38
  bidBlockHash: RootHex | null;
38
39
  }
40
+ | {
41
+ code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_ROOT_MISMATCH;
42
+ envelopeRequestsRoot: RootHex;
43
+ bidRequestsRoot: RootHex;
44
+ }
39
45
  | {code: ExecutionPayloadEnvelopeErrorCode.INVALID_SIGNATURE}
40
46
  | {code: ExecutionPayloadEnvelopeErrorCode.PAYLOAD_ENVELOPE_INPUT_MISSING; blockRoot: RootHex};
41
47
 
@@ -8,7 +8,6 @@ import {
8
8
  ProtoArray,
9
9
  ProtoBlock,
10
10
  ForkChoiceOpts as RawForkChoiceOpts,
11
- getCheckpointPayloadStatus,
12
11
  } from "@lodestar/fork-choice";
13
12
  import {ZERO_HASH_HEX} from "@lodestar/params";
14
13
  import {
@@ -104,12 +103,6 @@ export function initializeForkChoiceFromFinalizedState(
104
103
 
105
104
  const isForkPostGloas = computeEpochAtSlot(state.slot) >= config.GLOAS_FORK_EPOCH;
106
105
 
107
- // Determine justified checkpoint payload status
108
- const justifiedPayloadStatus = getCheckpointPayloadStatus(config, state, justifiedCheckpoint.epoch);
109
-
110
- // Determine finalized checkpoint payload status
111
- const finalizedPayloadStatus = getCheckpointPayloadStatus(config, state, finalizedCheckpoint.epoch);
112
-
113
106
  return new forkchoiceConstructor(
114
107
  config,
115
108
 
@@ -119,8 +112,6 @@ export function initializeForkChoiceFromFinalizedState(
119
112
  finalizedCheckpoint,
120
113
  justifiedBalances,
121
114
  justifiedBalancesGetter,
122
- justifiedPayloadStatus,
123
- finalizedPayloadStatus,
124
115
  {
125
116
  onJustified: (cp) => emitter.emit(ChainEvent.forkChoiceJustified, cp),
126
117
  onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp),
@@ -146,7 +137,9 @@ export function initializeForkChoiceFromFinalizedState(
146
137
 
147
138
  ...(isStatePostBellatrix(state) && state.isExecutionStateType && state.isMergeTransitionComplete
148
139
  ? {
149
- executionPayloadBlockHash: toRootHex(state.latestBlockHash),
140
+ executionPayloadBlockHash: isStatePostGloas(state)
141
+ ? toRootHex(state.latestBlockHash)
142
+ : toRootHex(state.latestExecutionPayloadHeader.blockHash),
150
143
  // TODO GLOAS: executionPayloadNumber is not tracked in BeaconState post-gloas (EIP-7732 removed
151
144
  // latestExecutionPayloadHeader). Using 0 as unavailable fallback until a solution is found.
152
145
  executionPayloadNumber: isStatePostGloas(state) ? 0 : state.payloadBlockNumber,
@@ -155,7 +148,7 @@ export function initializeForkChoiceFromFinalizedState(
155
148
  : {executionPayloadBlockHash: null, executionStatus: ExecutionStatus.PreMerge}),
156
149
 
157
150
  dataAvailabilityStatus: DataAvailabilityStatus.PreData,
158
- payloadStatus: isForkPostGloas ? PayloadStatus.PENDING : PayloadStatus.FULL, // TODO GLOAS: Post-gloas how do we know if the checkpoint payload is FULL or EMPTY?
151
+ payloadStatus: isForkPostGloas ? PayloadStatus.PENDING : PayloadStatus.FULL,
159
152
  parentBlockHash: isStatePostGloas(state) ? toRootHex(state.latestBlockHash) : null,
160
153
  },
161
154
  currentSlot
@@ -202,19 +195,12 @@ export function initializeForkChoiceFromUnfinalizedState(
202
195
 
203
196
  const isForkPostGloas = computeEpochAtSlot(unfinalizedState.slot) >= config.GLOAS_FORK_EPOCH;
204
197
 
205
- // For unfinalized state, use getCheckpointPayloadStatus to determine the correct status.
206
- // It checks state.execution_payload_availability to determine EMPTY vs FULL.
207
- const justifiedPayloadStatus = getCheckpointPayloadStatus(config, unfinalizedState, justifiedCheckpoint.epoch);
208
- const finalizedPayloadStatus = getCheckpointPayloadStatus(config, unfinalizedState, finalizedCheckpoint.epoch);
209
-
210
198
  const store = new ForkChoiceStore(
211
199
  currentSlot,
212
200
  justifiedCheckpoint,
213
201
  finalizedCheckpoint,
214
202
  justifiedBalances,
215
203
  justifiedBalancesGetter,
216
- justifiedPayloadStatus,
217
- finalizedPayloadStatus,
218
204
  {
219
205
  onJustified: (cp) => emitter.emit(ChainEvent.forkChoiceJustified, cp),
220
206
  onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp),
@@ -243,7 +229,9 @@ export function initializeForkChoiceFromUnfinalizedState(
243
229
  unfinalizedState.isExecutionStateType &&
244
230
  unfinalizedState.isMergeTransitionComplete
245
231
  ? {
246
- executionPayloadBlockHash: toRootHex(unfinalizedState.latestBlockHash),
232
+ executionPayloadBlockHash: isStatePostGloas(unfinalizedState)
233
+ ? toRootHex(unfinalizedState.latestBlockHash)
234
+ : toRootHex(unfinalizedState.latestExecutionPayloadHeader.blockHash),
247
235
  // TODO GLOAS: executionPayloadNumber is not tracked in BeaconState post-gloas (EIP-7732 removed
248
236
  // latestExecutionPayloadHeader). Using 0 as unavailable fallback until a solution is found.
249
237
  executionPayloadNumber: isStatePostGloas(unfinalizedState) ? 0 : unfinalizedState.payloadBlockNumber,
@@ -252,7 +240,7 @@ export function initializeForkChoiceFromUnfinalizedState(
252
240
  : {executionPayloadBlockHash: null, executionStatus: ExecutionStatus.PreMerge}),
253
241
 
254
242
  dataAvailabilityStatus: DataAvailabilityStatus.PreData,
255
- payloadStatus: isForkPostGloas ? PayloadStatus.PENDING : PayloadStatus.FULL, // TODO GLOAS: Post-gloas how do we know if the checkpoint payload is FULL or EMPTY?
243
+ payloadStatus: isForkPostGloas ? PayloadStatus.PENDING : PayloadStatus.FULL,
256
244
  parentBlockHash: isStatePostGloas(unfinalizedState) ? toRootHex(unfinalizedState.latestBlockHash) : null,
257
245
  };
258
246
 
@@ -1,6 +1,6 @@
1
1
  import {Type} from "@chainsafe/ssz";
2
2
  import {BeaconConfig} from "@lodestar/config";
3
- import {CheckpointWithHex, CheckpointWithPayloadStatus, IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
3
+ import {CheckpointWithHex, IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
4
4
  import {EpochShuffling, IBeaconStateView, PubkeyCache} from "@lodestar/state-transition";
5
5
  import {
6
6
  BeaconBlock,
@@ -18,6 +18,7 @@ import {
18
18
  altair,
19
19
  capella,
20
20
  deneb,
21
+ electra,
21
22
  gloas,
22
23
  phase0,
23
24
  rewards,
@@ -195,7 +196,7 @@ export interface IBeaconChain {
195
196
  ): {state: IBeaconStateView; executionOptimistic: boolean; finalized: boolean} | null;
196
197
  /** Return state bytes by checkpoint */
197
198
  getStateOrBytesByCheckpoint(
198
- checkpoint: CheckpointWithPayloadStatus
199
+ checkpoint: CheckpointWithHex
199
200
  ): Promise<{state: IBeaconStateView | Uint8Array; executionOptimistic: boolean; finalized: boolean} | null>;
200
201
 
201
202
  /**
@@ -231,6 +232,7 @@ export interface IBeaconChain {
231
232
  blockSlot: Slot,
232
233
  blockRootHex: string
233
234
  ): Promise<gloas.SignedExecutionPayloadEnvelope | null>;
235
+ getParentExecutionRequests(parentBlockSlot: Slot, parentBlockRootHex: RootHex): Promise<electra.ExecutionRequests>;
234
236
 
235
237
  produceCommonBlockBody(blockAttributes: BlockAttributes): Promise<CommonBlockBody>;
236
238
  produceBlock(blockAttributes: BlockAttributes & {commonBlockBodyPromise: Promise<CommonBlockBody>}): Promise<{
@@ -1,6 +1,6 @@
1
1
  import {routes} from "@lodestar/api";
2
2
  import {ChainForkConfig} from "@lodestar/config";
3
- import {PayloadStatus, getSafeExecutionBlockHash} from "@lodestar/fork-choice";
3
+ import {getSafeExecutionBlockHash} from "@lodestar/fork-choice";
4
4
  import {ForkPostBellatrix, ForkSeq, SLOTS_PER_EPOCH, isForkPostBellatrix} from "@lodestar/params";
5
5
  import {
6
6
  IBeaconStateView,
@@ -8,8 +8,9 @@ import {
8
8
  computeEpochAtSlot,
9
9
  computeTimeAtSlot,
10
10
  isStatePostBellatrix,
11
+ isStatePostGloas,
11
12
  } from "@lodestar/state-transition";
12
- import {Slot} from "@lodestar/types";
13
+ import {Bytes32, Slot, electra} from "@lodestar/types";
13
14
  import {Logger, fromHex, isErrorAborted, sleep} from "@lodestar/utils";
14
15
  import {GENESIS_SLOT, ZERO_HASH_HEX} from "../constants/constants.js";
15
16
  import {BuilderStatus} from "../execution/builder/http.js";
@@ -81,6 +82,8 @@ export class PrepareNextSlotScheduler {
81
82
  // calling updateHead() here before we produce a block to reduce reorg possibility
82
83
  const headBlock = this.chain.recomputeForkChoiceHead(ForkchoiceCaller.prepareNextSlot);
83
84
  const {slot: headSlot, blockRoot: headRoot} = headBlock;
85
+ // may be updated below if we predict a proposer-boost-reorg
86
+ let updatedHead = headBlock;
84
87
 
85
88
  // PS: previously this was comparing slots, but that gave no leway on the skipped
86
89
  // slots on epoch bounday. Making it more fluid.
@@ -123,7 +126,6 @@ export class PrepareNextSlotScheduler {
123
126
  const proposerIndex = prepareState.getBeaconProposer(prepareSlot);
124
127
  const feeRecipient = this.chain.beaconProposerCache.get(proposerIndex);
125
128
  let updatedPrepareState = prepareState;
126
- let updatedHeadRoot = headRoot;
127
129
 
128
130
  if (feeRecipient) {
129
131
  // If we are proposing next slot, we need to predict if we can proposer-boost-reorg or not
@@ -146,7 +148,7 @@ export class PrepareNextSlotScheduler {
146
148
  {dontTransferCache: !isEpochTransition},
147
149
  RegenCaller.predictProposerHead
148
150
  );
149
- updatedHeadRoot = proposerHeadRoot;
151
+ updatedHead = proposerHead;
150
152
  }
151
153
 
152
154
  // Update the builder status, if enabled shoot an api call to check status
@@ -156,29 +158,56 @@ export class PrepareNextSlotScheduler {
156
158
  this.logger.error("Builder disabled as the check status api failed", {prepareSlot}, e as Error);
157
159
  });
158
160
  }
161
+ }
162
+
163
+ if (!isStatePostBellatrix(updatedPrepareState)) {
164
+ throw new Error("Expected Bellatrix state for payload attributes");
165
+ }
159
166
 
167
+ let parentBlockHash: Bytes32;
168
+ let isExtendingPayload = false;
169
+ if (isStatePostGloas(updatedPrepareState)) {
170
+ isExtendingPayload = this.chain.forkChoice.shouldExtendPayload(updatedHead.blockRoot);
171
+ parentBlockHash = isExtendingPayload
172
+ ? updatedPrepareState.latestExecutionPayloadBid.blockHash
173
+ : updatedPrepareState.latestExecutionPayloadBid.parentBlockHash;
174
+ } else {
175
+ parentBlockHash = updatedPrepareState.latestExecutionPayloadHeader.blockHash;
176
+ }
177
+
178
+ // Reused by the SSE emit below to avoid a second DB lookup on cache miss
179
+ let parentExecutionRequests: electra.ExecutionRequests | undefined;
180
+
181
+ if (feeRecipient) {
160
182
  const preparationTime =
161
183
  computeTimeAtSlot(this.config, prepareSlot, this.chain.genesisTime) - Date.now() / 1000;
162
184
  this.metrics?.blockPayload.payloadAdvancePrepTime.observe(preparationTime);
163
- if (!isStatePostBellatrix(updatedPrepareState)) {
164
- throw new Error("Expected Bellatrix state for payload preparation");
165
- }
166
185
 
167
186
  const safeBlockHash = getSafeExecutionBlockHash(this.chain.forkChoice);
168
187
  const finalizedBlockHash =
169
188
  this.chain.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
189
+
190
+ if (isExtendingPayload) {
191
+ parentExecutionRequests = await this.chain.getParentExecutionRequests(
192
+ updatedHead.slot,
193
+ updatedHead.blockRoot
194
+ );
195
+ }
196
+
170
197
  // awaiting here instead of throwing an async call because there is no other task
171
- // left for scheduler and this gives nice sematics to catch and log errors in the
198
+ // left for scheduler and this gives nice semantics to catch and log errors in the
172
199
  // try/catch wrapper here.
173
200
  await prepareExecutionPayload(
174
201
  this.chain,
175
202
  this.logger,
176
203
  fork as ForkPostBellatrix, // State is of execution type
177
- fromHex(updatedHeadRoot),
204
+ fromHex(updatedHead.blockRoot),
205
+ parentBlockHash,
178
206
  safeBlockHash,
179
207
  finalizedBlockHash,
180
208
  updatedPrepareState,
181
- feeRecipient
209
+ feeRecipient,
210
+ parentExecutionRequests
182
211
  );
183
212
  this.logger.verbose("PrepareNextSlotScheduler prepared new payload", {
184
213
  prepareSlot,
@@ -187,24 +216,38 @@ export class PrepareNextSlotScheduler {
187
216
  });
188
217
  }
189
218
 
190
- if (!isStatePostBellatrix(updatedPrepareState)) {
191
- throw new Error("Expected Bellatrix state for payload attributes");
219
+ if (ForkSeq[fork] >= ForkSeq.gloas) {
220
+ // Cutoff = slot of the parent of the block we'll actually build on (post-reorg).
221
+ // Steady state: cache holds just 2 entries — head (parent for next-slot production)
222
+ // and head.parent (proposer-boost-reorg fallback). Anything older is evicted.
223
+ const updatedHeadParent = this.chain.forkChoice.getBlockHexDefaultStatus(updatedHead.parentRoot);
224
+ if (updatedHeadParent) {
225
+ this.chain.seenPayloadEnvelopeInputCache.pruneBelow(updatedHeadParent.slot);
226
+ }
192
227
  }
193
228
 
194
229
  this.computeStateHashTreeRoot(updatedPrepareState, isEpochTransition);
195
230
 
196
- // If emitPayloadAttributes is true emit a SSE payloadAttributes event
231
+ // If emitPayloadAttributes is true emit a SSE payloadAttributes event for
232
+ // every slot. Without the flag, only emit the event if we are proposing in the next slot.
197
233
  if (
198
- this.chain.opts.emitPayloadAttributes === true &&
234
+ (feeRecipient || this.chain.opts.emitPayloadAttributes === true) &&
199
235
  this.chain.emitter.listenerCount(routes.events.EventType.payloadAttributes)
200
236
  ) {
237
+ // if we didn't fetch above (not proposing), SSE still needs it here
238
+ if (!parentExecutionRequests && isExtendingPayload) {
239
+ parentExecutionRequests = await this.chain.getParentExecutionRequests(
240
+ updatedHead.slot,
241
+ updatedHead.blockRoot
242
+ );
243
+ }
201
244
  const data = getPayloadAttributesForSSE(fork as ForkPostBellatrix, this.chain, {
202
245
  prepareState: updatedPrepareState,
203
246
  prepareSlot,
204
- parentBlockRoot: fromHex(headRoot),
205
- // The likely consumers of this API are builders and will anyway ignore the
206
- // feeRecipient, so just pass zero hash for now till a real use case arises
207
- feeRecipient: "0x0000000000000000000000000000000000000000000000000000000000000000",
247
+ parentBlockRoot: fromHex(updatedHead.blockRoot),
248
+ parentBlockHash,
249
+ feeRecipient: feeRecipient ?? "0x0000000000000000000000000000000000000000",
250
+ parentExecutionRequests,
208
251
  });
209
252
  this.chain.emitter.emit(routes.events.EventType.payloadAttributes, {data, version: fork});
210
253
  }
@@ -217,11 +260,7 @@ export class PrepareNextSlotScheduler {
217
260
  // + if next slot is a skipped slot, it'd help getting target checkpoint state faster to validate attestations
218
261
  if (isEpochTransition) {
219
262
  this.metrics?.precomputeNextEpochTransition.count.inc({result: "success"}, 1);
220
- // Determine payloadPresent from head block's payload status
221
- // Pre-Gloas: payloadStatus is always FULL → payloadPresent = true
222
- // Post-Gloas: FULL → true, EMPTY → false, PENDING → false (conservative, treat as block state)
223
- const payloadPresent = headBlock.payloadStatus === PayloadStatus.FULL;
224
- const previousHits = this.chain.regen.updatePreComputedCheckpoint(headRoot, nextEpoch, payloadPresent);
263
+ const previousHits = this.chain.regen.updatePreComputedCheckpoint(headRoot, nextEpoch);
225
264
  if (previousHits === 0) {
226
265
  this.metrics?.precomputeNextEpochTransition.waste.inc();
227
266
  }