@lodestar/beacon-node 1.43.0-dev.aef3645690 → 1.43.0-dev.b7d69444c9

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 (227) 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 +27 -35
  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 +26 -39
  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/attestationError.d.ts +8 -1
  67. package/lib/chain/errors/attestationError.d.ts.map +1 -1
  68. package/lib/chain/errors/attestationError.js +4 -0
  69. package/lib/chain/errors/attestationError.js.map +1 -1
  70. package/lib/chain/errors/executionPayloadBid.d.ts +5 -0
  71. package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
  72. package/lib/chain/errors/executionPayloadBid.js +1 -0
  73. package/lib/chain/errors/executionPayloadBid.js.map +1 -1
  74. package/lib/chain/errors/executionPayloadEnvelope.d.ts +5 -0
  75. package/lib/chain/errors/executionPayloadEnvelope.d.ts.map +1 -1
  76. package/lib/chain/errors/executionPayloadEnvelope.js +1 -0
  77. package/lib/chain/errors/executionPayloadEnvelope.js.map +1 -1
  78. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  79. package/lib/chain/forkChoice/index.js +11 -15
  80. package/lib/chain/forkChoice/index.js.map +1 -1
  81. package/lib/chain/interface.d.ts +4 -3
  82. package/lib/chain/interface.d.ts.map +1 -1
  83. package/lib/chain/interface.js.map +1 -1
  84. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  85. package/lib/chain/prepareNextSlot.js +48 -22
  86. package/lib/chain/prepareNextSlot.js.map +1 -1
  87. package/lib/chain/produceBlock/computeNewStateRoot.d.ts +3 -9
  88. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  89. package/lib/chain/produceBlock/computeNewStateRoot.js +5 -32
  90. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  91. package/lib/chain/produceBlock/produceBlockBody.d.ts +4 -8
  92. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  93. package/lib/chain/produceBlock/produceBlockBody.js +48 -23
  94. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  95. package/lib/chain/regen/errors.d.ts +1 -11
  96. package/lib/chain/regen/errors.d.ts.map +1 -1
  97. package/lib/chain/regen/errors.js +0 -2
  98. package/lib/chain/regen/errors.js.map +1 -1
  99. package/lib/chain/regen/interface.d.ts +6 -12
  100. package/lib/chain/regen/interface.d.ts.map +1 -1
  101. package/lib/chain/regen/queued.d.ts +6 -11
  102. package/lib/chain/regen/queued.d.ts.map +1 -1
  103. package/lib/chain/regen/queued.js +8 -40
  104. package/lib/chain/regen/queued.js.map +1 -1
  105. package/lib/chain/regen/regen.d.ts +0 -5
  106. package/lib/chain/regen/regen.d.ts.map +1 -1
  107. package/lib/chain/regen/regen.js +7 -34
  108. package/lib/chain/regen/regen.js.map +1 -1
  109. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +11 -4
  110. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  111. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +20 -18
  112. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  113. package/lib/chain/stateCache/datastore/db.d.ts +5 -4
  114. package/lib/chain/stateCache/datastore/db.d.ts.map +1 -1
  115. package/lib/chain/stateCache/datastore/db.js +10 -32
  116. package/lib/chain/stateCache/datastore/db.js.map +1 -1
  117. package/lib/chain/stateCache/datastore/file.d.ts +1 -1
  118. package/lib/chain/stateCache/datastore/file.d.ts.map +1 -1
  119. package/lib/chain/stateCache/datastore/file.js +5 -5
  120. package/lib/chain/stateCache/datastore/file.js.map +1 -1
  121. package/lib/chain/stateCache/datastore/types.d.ts +1 -1
  122. package/lib/chain/stateCache/datastore/types.d.ts.map +1 -1
  123. package/lib/chain/stateCache/fifoBlockStateCache.d.ts +1 -7
  124. package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
  125. package/lib/chain/stateCache/fifoBlockStateCache.js +0 -8
  126. package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
  127. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +13 -30
  128. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  129. package/lib/chain/stateCache/persistentCheckpointsCache.js +120 -216
  130. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  131. package/lib/chain/stateCache/types.d.ts +8 -15
  132. package/lib/chain/stateCache/types.d.ts.map +1 -1
  133. package/lib/chain/stateCache/types.js.map +1 -1
  134. package/lib/chain/validation/aggregateAndProof.js +12 -0
  135. package/lib/chain/validation/aggregateAndProof.js.map +1 -1
  136. package/lib/chain/validation/attestation.d.ts.map +1 -1
  137. package/lib/chain/validation/attestation.js +12 -0
  138. package/lib/chain/validation/attestation.js.map +1 -1
  139. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  140. package/lib/chain/validation/executionPayloadBid.js +13 -1
  141. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  142. package/lib/chain/validation/executionPayloadEnvelope.d.ts.map +1 -1
  143. package/lib/chain/validation/executionPayloadEnvelope.js +21 -11
  144. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  145. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  146. package/lib/chain/validation/payloadAttestationMessage.js +4 -3
  147. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  148. package/lib/db/repositories/executionPayloadEnvelopeArchive.js +1 -1
  149. package/lib/db/repositories/executionPayloadEnvelopeArchive.js.map +1 -1
  150. package/lib/execution/engine/http.d.ts.map +1 -1
  151. package/lib/execution/engine/http.js +21 -14
  152. package/lib/execution/engine/http.js.map +1 -1
  153. package/lib/execution/engine/interface.d.ts +1 -0
  154. package/lib/execution/engine/interface.d.ts.map +1 -1
  155. package/lib/execution/engine/mock.d.ts.map +1 -1
  156. package/lib/execution/engine/mock.js +6 -0
  157. package/lib/execution/engine/mock.js.map +1 -1
  158. package/lib/execution/engine/types.d.ts +20 -0
  159. package/lib/execution/engine/types.d.ts.map +1 -1
  160. package/lib/execution/engine/types.js +18 -0
  161. package/lib/execution/engine/types.js.map +1 -1
  162. package/lib/network/gossip/topic.d.ts +23 -2
  163. package/lib/network/gossip/topic.d.ts.map +1 -1
  164. package/lib/network/network.js +1 -1
  165. package/lib/network/network.js.map +1 -1
  166. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  167. package/lib/network/processor/gossipHandlers.js +22 -6
  168. package/lib/network/processor/gossipHandlers.js.map +1 -1
  169. package/lib/node/nodejs.d.ts.map +1 -1
  170. package/lib/node/nodejs.js +4 -2
  171. package/lib/node/nodejs.js.map +1 -1
  172. package/lib/util/sszBytes.d.ts.map +1 -1
  173. package/lib/util/sszBytes.js +16 -3
  174. package/lib/util/sszBytes.js.map +1 -1
  175. package/package.json +16 -16
  176. package/src/api/impl/beacon/blocks/index.ts +1 -4
  177. package/src/api/impl/beacon/state/utils.ts +2 -2
  178. package/src/api/impl/validator/index.ts +3 -6
  179. package/src/chain/GetBlobsTracker.ts +1 -2
  180. package/src/chain/archiveStore/archiveStore.ts +5 -5
  181. package/src/chain/archiveStore/interface.ts +4 -4
  182. package/src/chain/archiveStore/strategies/frequencyStateArchiveStrategy.ts +6 -8
  183. package/src/chain/archiveStore/utils/archiveBlocks.ts +153 -94
  184. package/src/chain/blocks/importBlock.ts +26 -39
  185. package/src/chain/blocks/importExecutionPayload.ts +77 -103
  186. package/src/chain/blocks/index.ts +1 -2
  187. package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +27 -0
  188. package/src/chain/blocks/payloadEnvelopeProcessor.ts +6 -5
  189. package/src/chain/blocks/types.ts +15 -26
  190. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +129 -0
  191. package/src/chain/blocks/verifyPayloadsDataAvailability.ts +38 -0
  192. package/src/chain/blocks/writePayloadEnvelopeInputToDb.ts +9 -18
  193. package/src/chain/chain.ts +37 -62
  194. package/src/chain/emitter.ts +15 -3
  195. package/src/chain/errors/attestationError.ts +6 -1
  196. package/src/chain/errors/executionPayloadBid.ts +6 -0
  197. package/src/chain/errors/executionPayloadEnvelope.ts +6 -0
  198. package/src/chain/forkChoice/index.ts +8 -20
  199. package/src/chain/interface.ts +4 -2
  200. package/src/chain/prepareNextSlot.ts +62 -23
  201. package/src/chain/produceBlock/computeNewStateRoot.ts +6 -43
  202. package/src/chain/produceBlock/produceBlockBody.ts +66 -26
  203. package/src/chain/regen/errors.ts +1 -6
  204. package/src/chain/regen/interface.ts +6 -12
  205. package/src/chain/regen/queued.ts +12 -48
  206. package/src/chain/regen/regen.ts +8 -36
  207. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +22 -20
  208. package/src/chain/stateCache/datastore/db.ts +10 -33
  209. package/src/chain/stateCache/datastore/file.ts +5 -6
  210. package/src/chain/stateCache/datastore/types.ts +2 -3
  211. package/src/chain/stateCache/fifoBlockStateCache.ts +1 -10
  212. package/src/chain/stateCache/persistentCheckpointsCache.ts +139 -247
  213. package/src/chain/stateCache/types.ts +8 -14
  214. package/src/chain/validation/aggregateAndProof.ts +13 -0
  215. package/src/chain/validation/attestation.ts +13 -0
  216. package/src/chain/validation/executionPayloadBid.ts +14 -0
  217. package/src/chain/validation/executionPayloadEnvelope.ts +22 -12
  218. package/src/chain/validation/payloadAttestationMessage.ts +5 -3
  219. package/src/db/repositories/executionPayloadEnvelopeArchive.ts +1 -1
  220. package/src/execution/engine/http.ts +21 -14
  221. package/src/execution/engine/interface.ts +1 -0
  222. package/src/execution/engine/mock.ts +8 -1
  223. package/src/execution/engine/types.ts +41 -0
  224. package/src/network/network.ts +1 -1
  225. package/src/network/processor/gossipHandlers.ts +26 -10
  226. package/src/node/nodejs.ts +4 -2
  227. package/src/util/sszBytes.ts +21 -3
@@ -2,17 +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 {
6
- CheckpointWithPayloadStatus,
7
- IForkChoice,
8
- PayloadStatus,
9
- ProtoBlock,
10
- UpdateHeadOpt,
11
- getCheckpointPayloadStatus,
12
- } from "@lodestar/fork-choice";
5
+ import {CheckpointWithHex, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice";
13
6
  import {LoggerNode} from "@lodestar/logger/node";
14
7
  import {
15
- BUILDER_INDEX_SELF_BUILD,
16
8
  EFFECTIVE_BALANCE_INCREMENT,
17
9
  type ForkPostFulu,
18
10
  type ForkPostGloas,
@@ -31,7 +23,6 @@ import {
31
23
  getEffectiveBalancesFromStateBytes,
32
24
  isStatePostAltair,
33
25
  isStatePostElectra,
34
- isStatePostGloas,
35
26
  } from "@lodestar/state-transition";
36
27
  import {
37
28
  BeaconBlock,
@@ -48,6 +39,7 @@ import {
48
39
  ValidatorIndex,
49
40
  Wei,
50
41
  deneb,
42
+ electra,
51
43
  gloas,
52
44
  isBlindedBeaconBlock,
53
45
  phase0,
@@ -100,8 +92,8 @@ import {
100
92
  } from "./opPools/index.js";
101
93
  import {IChainOptions} from "./options.js";
102
94
  import {PrepareNextSlotScheduler} from "./prepareNextSlot.js";
103
- import {computeNewStateRoot, computePayloadEnvelopeStateRoot} from "./produceBlock/computeNewStateRoot.js";
104
- 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";
105
97
  import {BlockAttributes, produceBlockBody, produceCommonBlockBody} from "./produceBlock/produceBlockBody.js";
106
98
  import {QueuedStateRegenerator, RegenCaller} from "./regen/index.js";
107
99
  import {ReprocessController} from "./reprocess.js";
@@ -125,7 +117,7 @@ import {DbCPStateDatastore, checkpointToDatastoreKey} from "./stateCache/datasto
125
117
  import {FileCPStateDatastore} from "./stateCache/datastore/file.js";
126
118
  import {CPStateDatastore} from "./stateCache/datastore/types.js";
127
119
  import {FIFOBlockStateCache} from "./stateCache/fifoBlockStateCache.js";
128
- import {PersistentCheckpointStateCache, fcCheckpointToHexPayload} from "./stateCache/persistentCheckpointsCache.js";
120
+ import {PersistentCheckpointStateCache} from "./stateCache/persistentCheckpointsCache.js";
129
121
  import {CheckpointStateCache} from "./stateCache/types.js";
130
122
  import {ValidatorMonitor} from "./validatorMonitor.js";
131
123
 
@@ -390,8 +382,7 @@ export class BeaconChain implements IBeaconChain {
390
382
  const {checkpoint} = anchorState.computeAnchorCheckpoint();
391
383
  blockStateCache.add(anchorState);
392
384
  blockStateCache.setHeadState(anchorState);
393
- const payloadPresent = getCheckpointPayloadStatus(config, anchorState, checkpoint.epoch) === PayloadStatus.FULL;
394
- checkpointStateCache.add(checkpoint, anchorState, payloadPresent);
385
+ checkpointStateCache.add(checkpoint, anchorState);
395
386
 
396
387
  const forkChoice = initializeForkChoice(
397
388
  config,
@@ -685,16 +676,16 @@ export class BeaconChain implements IBeaconChain {
685
676
 
686
677
  // TODO GLOAS: Need to revisit the design of this api. Currently we just retrieve FULL state of the checkpoint for backwards compatibility.
687
678
  // because pre-gloas we always store FULL checkpoint state.
688
- const persistedKey = checkpointToDatastoreKey(checkpoint, true);
679
+ const persistedKey = checkpointToDatastoreKey(checkpoint);
689
680
  return this.cpStateDatastore.read(persistedKey);
690
681
  }
691
682
 
692
683
  getStateByCheckpoint(
693
- checkpoint: CheckpointWithPayloadStatus
684
+ checkpoint: CheckpointWithHex
694
685
  ): {state: IBeaconStateView; executionOptimistic: boolean; finalized: boolean} | null {
695
686
  // finalized or justified checkpoint states maynot be available with PersistentCheckpointStateCache, use getCheckpointStateOrBytes() api to get Uint8Array
696
- const checkpointHexPayload = fcCheckpointToHexPayload(checkpoint);
697
- const cachedStateCtx = this.regen.getCheckpointStateSync(checkpointHexPayload);
687
+ const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex};
688
+ const cachedStateCtx = this.regen.getCheckpointStateSync(checkpointHex);
698
689
  if (cachedStateCtx) {
699
690
  const block = this.forkChoice.getBlockDefaultStatus(
700
691
  ssz.phase0.BeaconBlockHeader.hashTreeRoot(cachedStateCtx.latestBlockHeader)
@@ -711,10 +702,10 @@ export class BeaconChain implements IBeaconChain {
711
702
  }
712
703
 
713
704
  async getStateOrBytesByCheckpoint(
714
- checkpoint: CheckpointWithPayloadStatus
705
+ checkpoint: CheckpointWithHex
715
706
  ): Promise<{state: IBeaconStateView | Uint8Array; executionOptimistic: boolean; finalized: boolean} | null> {
716
- const checkpointHexPayload = fcCheckpointToHexPayload(checkpoint);
717
- const cachedStateCtx = await this.regen.getCheckpointStateOrBytes(checkpointHexPayload);
707
+ const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex};
708
+ const cachedStateCtx = await this.regen.getCheckpointStateOrBytes(checkpointHex);
718
709
  if (cachedStateCtx) {
719
710
  const block = this.forkChoice.getBlockDefaultStatus(checkpoint.root);
720
711
  const finalizedEpoch = this.forkChoice.getFinalizedCheckpoint().epoch;
@@ -896,6 +887,17 @@ export class BeaconChain implements IBeaconChain {
896
887
  );
897
888
  }
898
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
+
899
901
  async getDataColumnSidecars(blockSlot: Slot, blockRootHex: string): Promise<DataColumnSidecar[]> {
900
902
  const fork = this.config.getForkName(blockSlot);
901
903
 
@@ -1070,7 +1072,7 @@ export class BeaconChain implements IBeaconChain {
1070
1072
  body,
1071
1073
  } as AssembledBlockType<T>;
1072
1074
 
1073
- const {newStateRoot, proposerReward, postBlockState} = computeNewStateRoot(this.metrics, state, block);
1075
+ const {newStateRoot, proposerReward} = computeNewStateRoot(this.metrics, state, block);
1074
1076
  block.stateRoot = newStateRoot;
1075
1077
  const blockRoot =
1076
1078
  produceResult.type === BlockType.Full
@@ -1079,26 +1081,9 @@ export class BeaconChain implements IBeaconChain {
1079
1081
  const blockRootHex = toRootHex(blockRoot);
1080
1082
 
1081
1083
  const fork = this.config.getForkName(slot);
1082
- if (isForkPostGloas(fork)) {
1083
- // TODO GLOAS: we should retire BlockType post-gloas, may need a new enum for self vs non-self built
1084
- if (produceResult.type !== BlockType.Full) {
1085
- throw Error(`Unexpected block type=${produceResult.type} for post-gloas fork=${fork}`);
1086
- }
1087
-
1088
- const gloasResult = produceResult as ProduceFullGloas;
1089
- const envelope: gloas.ExecutionPayloadEnvelope = {
1090
- payload: gloasResult.executionPayload,
1091
- executionRequests: gloasResult.executionRequests,
1092
- builderIndex: BUILDER_INDEX_SELF_BUILD,
1093
- beaconBlockRoot: blockRoot,
1094
- slot,
1095
- stateRoot: ZERO_HASH,
1096
- };
1097
- if (!isStatePostGloas(postBlockState)) {
1098
- throw Error(`Expected gloas+ post-state for execution payload envelope, got fork=${postBlockState.forkName}`);
1099
- }
1100
- const payloadEnvelopeStateRoot = computePayloadEnvelopeStateRoot(this.metrics, postBlockState, envelope);
1101
- 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}`);
1102
1087
  }
1103
1088
 
1104
1089
  // Track the produced block for consensus broadcast validations, later validation, etc.
@@ -1304,7 +1289,7 @@ export class BeaconChain implements IBeaconChain {
1304
1289
  * @param blockState state that declares justified checkpoint `checkpoint`
1305
1290
  */
1306
1291
  private justifiedBalancesGetter(
1307
- checkpoint: CheckpointWithPayloadStatus,
1292
+ checkpoint: CheckpointWithHex,
1308
1293
  blockState: IBeaconStateView
1309
1294
  ): EffectiveBalanceIncrements {
1310
1295
  this.metrics?.balancesCache.requests.inc();
@@ -1343,11 +1328,11 @@ export class BeaconChain implements IBeaconChain {
1343
1328
  * @param blockState state that declares justified checkpoint `checkpoint`
1344
1329
  */
1345
1330
  private closestJustifiedBalancesStateToCheckpoint(
1346
- checkpoint: CheckpointWithPayloadStatus,
1331
+ checkpoint: CheckpointWithHex,
1347
1332
  blockState: IBeaconStateView
1348
1333
  ): {state: IBeaconStateView; stateId: string; shouldWarn: boolean} {
1349
- const checkpointHexPayload = fcCheckpointToHexPayload(checkpoint);
1350
- const state = this.regen.getCheckpointStateSync(checkpointHexPayload);
1334
+ const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex};
1335
+ const state = this.regen.getCheckpointStateSync(checkpointHex);
1351
1336
  if (state) {
1352
1337
  return {state, stateId: "checkpoint_state", shouldWarn: false};
1353
1338
  }
@@ -1358,10 +1343,7 @@ export class BeaconChain implements IBeaconChain {
1358
1343
  }
1359
1344
 
1360
1345
  // Find a state in the same branch of checkpoint at same epoch. Balances should exactly the same
1361
- for (const descendantBlock of this.forkChoice.forwardIterateDescendants(
1362
- checkpoint.rootHex,
1363
- checkpoint.payloadStatus
1364
- )) {
1346
+ for (const descendantBlock of this.forkChoice.forwardIterateDescendantsDefaultStatus(checkpoint.rootHex)) {
1365
1347
  if (computeEpochAtSlot(descendantBlock.slot) === checkpoint.epoch) {
1366
1348
  const descendantBlockState = this.regen.getStateSync(descendantBlock.stateRoot);
1367
1349
  if (descendantBlockState) {
@@ -1377,10 +1359,7 @@ export class BeaconChain implements IBeaconChain {
1377
1359
 
1378
1360
  // Find a state in the same branch of checkpoint at a latter epoch. Balances are not the same, but should be close
1379
1361
  // Note: must call .forwardIterateDescendants() again since nodes are not sorted
1380
- for (const descendantBlock of this.forkChoice.forwardIterateDescendants(
1381
- checkpoint.rootHex,
1382
- checkpoint.payloadStatus
1383
- )) {
1362
+ for (const descendantBlock of this.forkChoice.forwardIterateDescendantsDefaultStatus(checkpoint.rootHex)) {
1384
1363
  if (computeEpochAtSlot(descendantBlock.slot) > checkpoint.epoch) {
1385
1364
  const descendantBlockState = this.regen.getStateSync(descendantBlock.stateRoot);
1386
1365
  if (descendantBlockState) {
@@ -1471,10 +1450,6 @@ export class BeaconChain implements IBeaconChain {
1471
1450
  private onClockEpoch(epoch: Epoch): void {
1472
1451
  this.metrics?.clockEpoch.set(epoch);
1473
1452
 
1474
- if (epoch === this.config.GLOAS_FORK_EPOCH) {
1475
- this.regen.upgradeForGloas(epoch);
1476
- }
1477
-
1478
1453
  this.seenAttesters.prune(epoch);
1479
1454
  this.seenAggregators.prune(epoch);
1480
1455
  this.seenPayloadAttesters.prune(epoch);
@@ -1488,7 +1463,7 @@ export class BeaconChain implements IBeaconChain {
1488
1463
  this.seenContributionAndProof.prune(head.slot);
1489
1464
  }
1490
1465
 
1491
- private onForkChoiceJustified(this: BeaconChain, cp: CheckpointWithPayloadStatus): void {
1466
+ private onForkChoiceJustified(this: BeaconChain, cp: CheckpointWithHex): void {
1492
1467
  this.logger.verbose("Fork choice justified", {epoch: cp.epoch, root: cp.rootHex});
1493
1468
  }
1494
1469
 
@@ -1499,7 +1474,7 @@ export class BeaconChain implements IBeaconChain {
1499
1474
  });
1500
1475
  }
1501
1476
 
1502
- private async onForkChoiceFinalized(this: BeaconChain, cp: CheckpointWithPayloadStatus): Promise<void> {
1477
+ private async onForkChoiceFinalized(this: BeaconChain, cp: CheckpointWithHex): Promise<void> {
1503
1478
  this.logger.verbose("Fork choice finalized", {epoch: cp.epoch, root: cp.rootHex});
1504
1479
  const finalizedSlot = computeStartSlotAtEpoch(cp.epoch);
1505
1480
  this.seenBlockProposers.prune(finalizedSlot);
@@ -1540,7 +1515,7 @@ export class BeaconChain implements IBeaconChain {
1540
1515
  }
1541
1516
  }
1542
1517
 
1543
- private async updateValidatorsCustodyRequirement(finalizedCheckpoint: CheckpointWithPayloadStatus): Promise<void> {
1518
+ private async updateValidatorsCustodyRequirement(finalizedCheckpoint: CheckpointWithHex): Promise<void> {
1544
1519
  if (this.custodyConfig.targetCustodyGroupCount === this.config.NUMBER_OF_CUSTODY_GROUPS) {
1545
1520
  // Custody requirements can only be increased, we can disable dynamic custody updates
1546
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
 
@@ -147,6 +147,10 @@ export enum AttestationErrorCode {
147
147
  * Gloas: Current slot attestation is marking payload as present
148
148
  */
149
149
  PREMATURELY_INDICATED_PAYLOAD_PRESENT = "ATTESTATION_ERROR_PREMATURELY_INDICATED_PAYLOAD_PRESENT",
150
+ /**
151
+ * Gloas: index-1 attestation but the execution payload has not been seen yet
152
+ */
153
+ EXECUTION_PAYLOAD_NOT_SEEN = "ATTESTATION_ERROR_EXECUTION_PAYLOAD_NOT_SEEN",
150
154
  }
151
155
 
152
156
  export type AttestationErrorType =
@@ -185,7 +189,8 @@ export type AttestationErrorType =
185
189
  | {code: AttestationErrorCode.NON_ZERO_ATTESTATION_DATA_INDEX}
186
190
  | {code: AttestationErrorCode.ATTESTER_NOT_IN_COMMITTEE}
187
191
  | {code: AttestationErrorCode.INVALID_PAYLOAD_STATUS_VALUE; attDataIndex: number}
188
- | {code: AttestationErrorCode.PREMATURELY_INDICATED_PAYLOAD_PRESENT};
192
+ | {code: AttestationErrorCode.PREMATURELY_INDICATED_PAYLOAD_PRESENT}
193
+ | {code: AttestationErrorCode.EXECUTION_PAYLOAD_NOT_SEEN; beaconBlockRoot: RootHex};
189
194
 
190
195
  export class AttestationError extends GossipActionError<AttestationErrorType> {
191
196
  getMetadata(): Record<string, string | number | null> {
@@ -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
  }