@lodestar/beacon-node 1.44.0-dev.1a8c38ee36 → 1.44.0-dev.1b4e24f1ee

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 (161) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +43 -5
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/pool/index.js +1 -1
  6. package/lib/api/impl/beacon/pool/index.js.map +1 -1
  7. package/lib/api/impl/config/constants.d.ts +1 -0
  8. package/lib/api/impl/config/constants.d.ts.map +1 -1
  9. package/lib/api/impl/config/constants.js +2 -1
  10. package/lib/api/impl/config/constants.js.map +1 -1
  11. package/lib/api/impl/debug/index.d.ts.map +1 -1
  12. package/lib/api/impl/debug/index.js +69 -12
  13. package/lib/api/impl/debug/index.js.map +1 -1
  14. package/lib/api/impl/lodestar/index.d.ts.map +1 -1
  15. package/lib/api/impl/lodestar/index.js +28 -0
  16. package/lib/api/impl/lodestar/index.js.map +1 -1
  17. package/lib/api/impl/validator/index.d.ts.map +1 -1
  18. package/lib/api/impl/validator/index.js +97 -45
  19. package/lib/api/impl/validator/index.js.map +1 -1
  20. package/lib/chain/archiveStore/archiveStore.d.ts +0 -1
  21. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  22. package/lib/chain/archiveStore/archiveStore.js +0 -4
  23. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  24. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  25. package/lib/chain/blocks/importBlock.js +5 -2
  26. package/lib/chain/blocks/importBlock.js.map +1 -1
  27. package/lib/chain/blocks/importExecutionPayload.js +1 -1
  28. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  29. package/lib/chain/chain.d.ts +1 -1
  30. package/lib/chain/chain.d.ts.map +1 -1
  31. package/lib/chain/chain.js +10 -2
  32. package/lib/chain/chain.js.map +1 -1
  33. package/lib/chain/errors/executionPayloadBid.d.ts +5 -0
  34. package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
  35. package/lib/chain/errors/executionPayloadBid.js +1 -0
  36. package/lib/chain/errors/executionPayloadBid.js.map +1 -1
  37. package/lib/chain/errors/payloadAttestation.d.ts +6 -0
  38. package/lib/chain/errors/payloadAttestation.d.ts.map +1 -1
  39. package/lib/chain/errors/payloadAttestation.js +1 -0
  40. package/lib/chain/errors/payloadAttestation.js.map +1 -1
  41. package/lib/chain/forkChoice/index.d.ts +4 -4
  42. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  43. package/lib/chain/forkChoice/index.js +7 -7
  44. package/lib/chain/forkChoice/index.js.map +1 -1
  45. package/lib/chain/opPools/executionPayloadBidPool.d.ts +4 -4
  46. package/lib/chain/opPools/executionPayloadBidPool.d.ts.map +1 -1
  47. package/lib/chain/opPools/executionPayloadBidPool.js +6 -4
  48. package/lib/chain/opPools/executionPayloadBidPool.js.map +1 -1
  49. package/lib/chain/options.d.ts.map +1 -1
  50. package/lib/chain/options.js +1 -0
  51. package/lib/chain/options.js.map +1 -1
  52. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  53. package/lib/chain/prepareNextSlot.js +2 -1
  54. package/lib/chain/prepareNextSlot.js.map +1 -1
  55. package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -1
  56. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  57. package/lib/chain/produceBlock/produceBlockBody.js +59 -16
  58. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  59. package/lib/chain/regen/interface.d.ts +2 -1
  60. package/lib/chain/regen/interface.d.ts.map +1 -1
  61. package/lib/chain/regen/interface.js +2 -0
  62. package/lib/chain/regen/interface.js.map +1 -1
  63. package/lib/chain/regen/queued.d.ts +0 -1
  64. package/lib/chain/regen/queued.d.ts.map +1 -1
  65. package/lib/chain/regen/queued.js +0 -4
  66. package/lib/chain/regen/queued.js.map +1 -1
  67. package/lib/chain/stateCache/fifoBlockStateCache.d.ts +0 -5
  68. package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
  69. package/lib/chain/stateCache/fifoBlockStateCache.js +0 -5
  70. package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
  71. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +1 -4
  72. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  73. package/lib/chain/stateCache/persistentCheckpointsCache.js +5 -2
  74. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  75. package/lib/chain/stateCache/types.d.ts +0 -2
  76. package/lib/chain/stateCache/types.d.ts.map +1 -1
  77. package/lib/chain/stateCache/types.js.map +1 -1
  78. package/lib/chain/validation/executionPayloadBid.js +34 -7
  79. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  80. package/lib/chain/validation/executionPayloadEnvelope.js +0 -2
  81. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  82. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  83. package/lib/chain/validation/payloadAttestationMessage.js +24 -4
  84. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  85. package/lib/network/gossip/topic.d.ts +19 -766
  86. package/lib/network/gossip/topic.d.ts.map +1 -1
  87. package/lib/network/interface.d.ts +1 -0
  88. package/lib/network/interface.d.ts.map +1 -1
  89. package/lib/network/network.d.ts +1 -0
  90. package/lib/network/network.d.ts.map +1 -1
  91. package/lib/network/network.js +5 -0
  92. package/lib/network/network.js.map +1 -1
  93. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  94. package/lib/network/processor/gossipHandlers.js +2 -2
  95. package/lib/network/processor/gossipHandlers.js.map +1 -1
  96. package/lib/network/processor/index.d.ts +2 -2
  97. package/lib/network/processor/index.d.ts.map +1 -1
  98. package/lib/network/processor/index.js +24 -22
  99. package/lib/network/processor/index.js.map +1 -1
  100. package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
  101. package/lib/network/reqresp/handlers/beaconBlocksByRange.js +9 -5
  102. package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
  103. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
  104. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +13 -3
  105. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
  106. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +1 -1
  107. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
  108. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts +2 -1
  109. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
  110. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +16 -6
  111. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
  112. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts +2 -1
  113. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
  114. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +15 -1
  115. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
  116. package/lib/network/reqresp/handlers/index.js +4 -4
  117. package/lib/network/reqresp/handlers/index.js.map +1 -1
  118. package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
  119. package/lib/network/reqresp/utils/dataColumnResponseValidation.js +22 -3
  120. package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
  121. package/lib/util/dataColumns.d.ts.map +1 -1
  122. package/lib/util/dataColumns.js +16 -11
  123. package/lib/util/dataColumns.js.map +1 -1
  124. package/package.json +14 -16
  125. package/src/api/impl/beacon/blocks/index.ts +49 -5
  126. package/src/api/impl/beacon/pool/index.ts +3 -1
  127. package/src/api/impl/config/constants.ts +2 -0
  128. package/src/api/impl/debug/index.ts +73 -12
  129. package/src/api/impl/lodestar/index.ts +30 -0
  130. package/src/api/impl/validator/index.ts +108 -47
  131. package/src/chain/archiveStore/archiveStore.ts +0 -5
  132. package/src/chain/blocks/importBlock.ts +10 -2
  133. package/src/chain/blocks/importExecutionPayload.ts +1 -1
  134. package/src/chain/chain.ts +12 -1
  135. package/src/chain/errors/executionPayloadBid.ts +2 -0
  136. package/src/chain/errors/payloadAttestation.ts +2 -0
  137. package/src/chain/forkChoice/index.ts +8 -0
  138. package/src/chain/opPools/executionPayloadBidPool.ts +10 -9
  139. package/src/chain/options.ts +1 -0
  140. package/src/chain/prepareNextSlot.ts +2 -1
  141. package/src/chain/produceBlock/produceBlockBody.ts +83 -18
  142. package/src/chain/regen/interface.ts +2 -1
  143. package/src/chain/regen/queued.ts +0 -5
  144. package/src/chain/stateCache/fifoBlockStateCache.ts +0 -6
  145. package/src/chain/stateCache/persistentCheckpointsCache.ts +6 -2
  146. package/src/chain/stateCache/types.ts +0 -2
  147. package/src/chain/validation/executionPayloadBid.ts +36 -7
  148. package/src/chain/validation/executionPayloadEnvelope.ts +0 -2
  149. package/src/chain/validation/payloadAttestationMessage.ts +26 -4
  150. package/src/network/interface.ts +1 -0
  151. package/src/network/network.ts +11 -0
  152. package/src/network/processor/gossipHandlers.ts +4 -2
  153. package/src/network/processor/index.ts +26 -26
  154. package/src/network/reqresp/handlers/beaconBlocksByRange.ts +12 -5
  155. package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +17 -3
  156. package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +1 -1
  157. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +22 -6
  158. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +20 -1
  159. package/src/network/reqresp/handlers/index.ts +4 -4
  160. package/src/network/reqresp/utils/dataColumnResponseValidation.ts +21 -3
  161. package/src/util/dataColumns.ts +17 -12
@@ -914,20 +914,38 @@ export function getValidatorApi(
914
914
  notWhileSyncing();
915
915
  await waitForSlot(slot);
916
916
 
917
- // TODO GLOAS: support producing blocks from builder bids
918
- const source = ProducedBlockSource.engine;
919
-
920
- // TODO GLOAS: needs to be updated after fork choice changes are merged
921
917
  const parentBlock = chain.getProposerHead(slot);
922
918
  const {blockRoot: parentBlockRootHex, slot: parentSlot} = parentBlock;
923
919
  const parentBlockRoot = fromHex(parentBlockRootHex);
924
920
  notOnOutOfRangeData(parentBlockRoot);
925
921
  metrics?.blockProductionSlotDelta.set(slot - parentSlot);
926
- metrics?.blockProductionRequests.inc({source});
927
922
 
928
923
  const graffitiBytes = toGraffitiBytes(
929
924
  graffiti ?? getDefaultGraffiti(getLodestarClientVersion(opts), chain.executionEngine.clientVersion, opts)
930
925
  );
926
+
927
+ // TODO GLOAS: respect builderSelection (MaxProfit, BuilderAlways, ExecutionAlways, etc.) to let
928
+ // the user control bid source preferences and value comparison. Also add external builder api
929
+ // support when it is implemented.
930
+ const isBuildingOnFull = chain.forkChoice.shouldBuildOnFull(parentBlock, slot);
931
+ const bidParentBlockHash = isBuildingOnFull ? parentBlock.executionPayloadBlockHash : parentBlock.parentBlockHash;
932
+ const builderBid = chain.executionPayloadBidPool.getBestBid(slot, bidParentBlockHash, parentBlockRootHex);
933
+
934
+ const logCtx = {
935
+ slot,
936
+ parentSlot,
937
+ parentBlockRoot: parentBlockRootHex,
938
+ parentBlockHash: parentBlock.executionPayloadBlockHash,
939
+ fork,
940
+ ...(builderBid !== null
941
+ ? {
942
+ bidValue: builderBid.message.value,
943
+ builderIndex: builderBid.message.builderIndex,
944
+ bidBlockHash: toRootHex(builderBid.message.blockHash),
945
+ }
946
+ : {}),
947
+ };
948
+
931
949
  const commonBlockBodyPromise = chain.produceCommonBlockBody({
932
950
  slot,
933
951
  parentBlock,
@@ -935,44 +953,76 @@ export function getValidatorApi(
935
953
  graffiti: graffitiBytes,
936
954
  });
937
955
 
938
- let timer: undefined | ((opts: {source: ProducedBlockSource}) => number);
939
- try {
940
- timer = metrics?.blockProductionTime.startTimer();
941
- const {block, executionPayloadValue, consensusBlockValue} = await chain.produceBlock({
942
- slot,
943
- parentBlock,
944
- randaoReveal,
945
- graffiti: graffitiBytes,
946
- feeRecipient,
947
- commonBlockBodyPromise,
948
- });
956
+ const baseAttrs = {
957
+ slot,
958
+ parentBlock,
959
+ randaoReveal,
960
+ graffiti: graffitiBytes,
961
+ feeRecipient,
962
+ commonBlockBodyPromise,
963
+ };
949
964
 
950
- metrics?.blockProductionSuccess.inc({source});
951
- metrics?.blockProductionNumAggregated.observe({source}, block.body.attestations.length);
952
- metrics?.blockProductionConsensusBlockValue.observe({source}, Number(formatWeiToEth(consensusBlockValue)));
953
- metrics?.blockProductionExecutionPayloadValue.observe({source}, Number(formatWeiToEth(executionPayloadValue)));
965
+ metrics?.blockProductionRequests.inc({source: ProducedBlockSource.engine});
966
+ if (builderBid !== null) {
967
+ metrics?.blockProductionRequests.inc({source: ProducedBlockSource.builder});
968
+ }
954
969
 
955
- const blockRoot = toRootHex(config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block));
956
- logger.verbose("Produced block", {
957
- slot,
958
- executionPayloadValue,
959
- consensusBlockValue,
960
- root: blockRoot,
961
- });
962
- if (chain.opts.persistProducedBlocks) {
963
- void chain.persistBlock(block, "produced_engine_block");
970
+ const timed = <T>(source: ProducedBlockSource, fn: () => Promise<T>): Promise<T> => {
971
+ const t = metrics?.blockProductionTime.startTimer();
972
+ return fn().finally(() => t?.({source}));
973
+ };
974
+
975
+ // Always build local block. If builder bid available, also build with it in parallel and prefer it.
976
+ const [engineResult, bidResult] = await Promise.allSettled([
977
+ timed(ProducedBlockSource.engine, () => chain.produceBlock(baseAttrs)),
978
+ builderBid !== null
979
+ ? timed(ProducedBlockSource.builder, () => chain.produceBlock({...baseAttrs, builderBid}))
980
+ : Promise.reject(),
981
+ ]);
982
+
983
+ let bestResult: typeof engineResult | null = null;
984
+ let source: ProducedBlockSource = ProducedBlockSource.engine;
985
+ if (builderBid !== null && bidResult.status === "fulfilled") {
986
+ source = ProducedBlockSource.builder;
987
+ bestResult = bidResult;
988
+ logger.info("Selected builder bid block", logCtx);
989
+ } else if (engineResult.status === "fulfilled") {
990
+ source = ProducedBlockSource.engine;
991
+ bestResult = engineResult;
992
+ if (builderBid !== null) {
993
+ logger.warn("Builder bid block production failed, using local block", logCtx);
964
994
  }
995
+ }
965
996
 
966
- return {
967
- data: block as gloas.BeaconBlock,
968
- meta: {
969
- version: fork,
970
- consensusBlockValue,
971
- },
972
- };
973
- } finally {
974
- timer?.({source});
997
+ if (bestResult === null || bestResult.status !== "fulfilled") {
998
+ const engineReason = engineResult.status === "rejected" ? engineResult.reason : undefined;
999
+ const bidReason = builderBid !== null && bidResult.status === "rejected" ? bidResult.reason : undefined;
1000
+ logger.error("Block production failed", {...logCtx, engineReason, bidReason});
1001
+ throw Error(`Block production failed: engine=${engineReason ?? "n/a"} builder=${bidReason ?? "n/a"}`);
1002
+ }
1003
+
1004
+ const {block, executionPayloadValue, consensusBlockValue} = bestResult.value;
1005
+
1006
+ metrics?.blockProductionSuccess.inc({source});
1007
+ metrics?.blockProductionNumAggregated.observe({source}, block.body.attestations.length);
1008
+ metrics?.blockProductionConsensusBlockValue.observe({source}, Number(formatWeiToEth(consensusBlockValue)));
1009
+ metrics?.blockProductionExecutionPayloadValue.observe({source}, Number(formatWeiToEth(executionPayloadValue)));
1010
+
1011
+ const blockRoot = toRootHex(config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block));
1012
+ logger.verbose("Produced block", {
1013
+ ...logCtx,
1014
+ executionPayloadValue,
1015
+ consensusBlockValue,
1016
+ root: blockRoot,
1017
+ });
1018
+ if (chain.opts.persistProducedBlocks) {
1019
+ void chain.persistBlock(block, "produced_engine_block");
975
1020
  }
1021
+
1022
+ return {
1023
+ data: block as gloas.BeaconBlock,
1024
+ meta: {version: fork, consensusBlockValue},
1025
+ };
976
1026
  },
977
1027
 
978
1028
  async produceAttestationData({committeeIndex, slot}) {
@@ -1060,23 +1110,34 @@ export function getValidatorApi(
1060
1110
  notWhileSyncing();
1061
1111
  await waitForSlot(slot);
1062
1112
 
1063
- const block = chain.forkChoice.getCanonicalBlockClosestLteSlot(slot);
1113
+ const block = chain.forkChoice.getCanonicalBlockAtSlot(slot);
1064
1114
  if (!block) {
1065
- throw new ApiError(404, `No canonical block found at or before slot=${slot}`);
1115
+ // No block is seen at slot. Return 404 so vc can skip casting payload attestation.
1116
+ throw new ApiError(404, `No canonical block found at slot=${slot}`);
1066
1117
  }
1067
1118
 
1068
- const blockIsForSlot = block.slot === slot;
1069
1119
  const payloadInput = chain.seenPayloadEnvelopeInputCache.get(block.blockRoot);
1070
1120
  // Spec: set payload_present only if the envelope was seen before get_payload_due_ms()
1071
1121
  // into the slot. Use the envelope's own arrival time (getPayloadEnvelopeSource), not
1072
1122
  // the input's creation time.
1073
1123
  const payloadDueSec = config.getPayloadDueMs() / 1000;
1074
- const payloadPresent =
1075
- blockIsForSlot &&
1076
- payloadInput !== undefined &&
1077
- payloadInput.hasPayloadEnvelope() &&
1078
- chain.clock.secFromSlot(slot, payloadInput.getPayloadEnvelopeSource().seenTimestampSec) < payloadDueSec;
1079
- const blobDataAvailable = blockIsForSlot && (payloadInput?.hasAllData() ?? false);
1124
+ const payloadSeenSec =
1125
+ payloadInput?.hasPayloadEnvelope() === true
1126
+ ? chain.clock.secFromSlot(slot, payloadInput.getPayloadEnvelopeSource().seenTimestampSec)
1127
+ : null;
1128
+ const payloadPresent = payloadSeenSec !== null && payloadSeenSec < payloadDueSec;
1129
+ const blobDataAvailable = payloadInput?.hasAllData() === true;
1130
+
1131
+ logger.debug("Produced payload attestation data", {
1132
+ slot,
1133
+ blockRoot: block.blockRoot,
1134
+ blockSlot: block.slot,
1135
+ payloadPresent,
1136
+ blobDataAvailable,
1137
+ hasPayloadInput: payloadInput !== undefined,
1138
+ payloadSeenSec,
1139
+ payloadDueSec,
1140
+ });
1080
1141
 
1081
1142
  return {
1082
1143
  data: {
@@ -30,7 +30,6 @@ export enum ArchiveStoreTask {
30
30
  PruneHistory = "prune_history",
31
31
  OnFinalizedCheckpoint = "on_finalized_checkpoint",
32
32
  MaybeArchiveState = "maybe_archive_state",
33
- RegenPruneOnFinalized = "regen_prune_on_finalized",
34
33
  ForkchoicePrune = "forkchoice_prune",
35
34
  UpdateBackfillRange = "update_backfill_range",
36
35
  }
@@ -229,10 +228,6 @@ export class ArchiveStore {
229
228
  await this.statesArchiverStrategy.maybeArchiveState(finalized, this.metrics);
230
229
  timer?.({source: ArchiveStoreTask.MaybeArchiveState});
231
230
 
232
- timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer();
233
- this.chain.regen.pruneOnFinalized(finalizedEpoch);
234
- timer?.({source: ArchiveStoreTask.RegenPruneOnFinalized});
235
-
236
231
  // tasks rely on extended fork choice
237
232
  timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer();
238
233
  const prunedBlocks = this.chain.forkChoice.prune(finalized.rootHex);
@@ -116,13 +116,19 @@ export async function importBlock(
116
116
  }
117
117
  executionStatus = parentBlock.executionStatus;
118
118
  }
119
+
120
+ // getBeaconProposerOrNull will return null if head state is more than one epoch away
121
+ // from block slot. We skip proposer boost canonical check as we cannot determine the canonical proposer
122
+ const expectedProposerIndex: number | null = this.getHeadState().getBeaconProposerOrNull(blockSlot);
123
+
119
124
  const blockSummary = this.forkChoice.onBlock(
120
125
  block.message,
121
126
  postState,
122
127
  blockDelaySec,
123
128
  currentSlot,
124
129
  executionStatus,
125
- dataAvailabilityStatus
130
+ dataAvailabilityStatus,
131
+ expectedProposerIndex
126
132
  );
127
133
 
128
134
  // This adds the state necessary to process the next block
@@ -257,8 +263,10 @@ export async function importBlock(
257
263
  if (ptcIndices.length > 0) {
258
264
  this.forkChoice.notifyPtcMessages(
259
265
  toRootHex(payloadAttestation.data.beaconBlockRoot),
266
+ payloadAttestation.data.slot,
260
267
  ptcIndices,
261
- payloadAttestation.data.payloadPresent
268
+ payloadAttestation.data.payloadPresent,
269
+ payloadAttestation.data.blobDataAvailable
262
270
  );
263
271
  }
264
272
  } catch (e) {
@@ -129,7 +129,7 @@ export async function importExecutionPayload(
129
129
 
130
130
  // 3. Regenerate state for envelope verification
131
131
  const blockState = await this.regen
132
- .getBlockSlotState(protoBlock, protoBlock.slot, {dontTransferCache: true}, RegenCaller.processBlock)
132
+ .getBlockSlotState(protoBlock, protoBlock.slot, {dontTransferCache: true}, RegenCaller.importExecutionPayload)
133
133
  .catch(() =>
134
134
  // only happen at the 1st batch of skipped slot checkpoint sync
135
135
  this.regen.getClosestHeadState(protoBlock)
@@ -2,7 +2,7 @@ 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 {CheckpointWithHex, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice";
5
+ import {CheckpointWithHex, ForkChoiceStateGetter, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice";
6
6
  import {LoggerNode} from "@lodestar/logger/node";
7
7
  import {
8
8
  EFFECTIVE_BALANCE_INCREMENT,
@@ -382,6 +382,14 @@ export class BeaconChain implements IBeaconChain {
382
382
  blockStateCache.setHeadState(anchorState);
383
383
  checkpointStateCache.add(checkpoint, anchorState);
384
384
 
385
+ const forkChoiceStateGetter: ForkChoiceStateGetter = ({stateRoot, checkpoint}) => {
386
+ if (stateRoot) return blockStateCache.get(stateRoot);
387
+
388
+ if (checkpoint) return checkpointStateCache.get({epoch: checkpoint.epoch, rootHex: checkpoint.rootHex});
389
+
390
+ return null;
391
+ };
392
+
385
393
  const forkChoice = initializeForkChoice(
386
394
  config,
387
395
  emitter,
@@ -390,6 +398,7 @@ export class BeaconChain implements IBeaconChain {
390
398
  isAnchorStateFinalized,
391
399
  opts,
392
400
  this.justifiedBalancesGetter.bind(this),
401
+ forkChoiceStateGetter,
393
402
  metrics,
394
403
  logger
395
404
  );
@@ -1049,6 +1058,7 @@ export class BeaconChain implements IBeaconChain {
1049
1058
  feeRecipient,
1050
1059
  commonBlockBodyPromise,
1051
1060
  parentBlock,
1061
+ builderBid,
1052
1062
  }: BlockAttributes & {commonBlockBodyPromise: Promise<CommonBlockBody>}
1053
1063
  ): Promise<{
1054
1064
  block: AssembledBlockType<T>;
@@ -1078,6 +1088,7 @@ export class BeaconChain implements IBeaconChain {
1078
1088
  proposerIndex,
1079
1089
  proposerPubKey,
1080
1090
  commonBlockBodyPromise,
1091
+ builderBid,
1081
1092
  }
1082
1093
  );
1083
1094
 
@@ -11,6 +11,7 @@ export enum ExecutionPayloadBidErrorCode {
11
11
  UNKNOWN_BLOCK_ROOT = "EXECUTION_PAYLOAD_BID_ERROR_UNKNOWN_BLOCK_ROOT",
12
12
  UNKNOWN_PARENT_BLOCK_HASH = "EXECUTION_PAYLOAD_BID_ERROR_UNKNOWN_PARENT_BLOCK_HASH",
13
13
  INVALID_SLOT = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SLOT",
14
+ NOT_LATER_THAN_PARENT = "EXECUTION_PAYLOAD_BID_ERROR_NOT_LATER_THAN_PARENT",
14
15
  INVALID_SIGNATURE = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SIGNATURE",
15
16
  NO_MATCHING_PROPOSER_PREFERENCES = "EXECUTION_PAYLOAD_BID_ERROR_NO_MATCHING_PROPOSER_PREFERENCES",
16
17
  PROPOSER_PREFERENCES_FEE_RECIPIENT_MISMATCH = "EXECUTION_PAYLOAD_BID_ERROR_PROPOSER_PREFERENCES_FEE_RECIPIENT_MISMATCH",
@@ -41,6 +42,7 @@ export type ExecutionPayloadBidErrorType =
41
42
  | {code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT; parentBlockRoot: RootHex}
42
43
  | {code: ExecutionPayloadBidErrorCode.UNKNOWN_PARENT_BLOCK_HASH; parentBlockHash: RootHex}
43
44
  | {code: ExecutionPayloadBidErrorCode.INVALID_SLOT; builderIndex: BuilderIndex; slot: Slot}
45
+ | {code: ExecutionPayloadBidErrorCode.NOT_LATER_THAN_PARENT; parentSlot: Slot; slot: Slot}
44
46
  | {code: ExecutionPayloadBidErrorCode.INVALID_SIGNATURE; builderIndex: BuilderIndex; slot: Slot}
45
47
  | {
46
48
  code: ExecutionPayloadBidErrorCode.NO_MATCHING_PROPOSER_PREFERENCES;
@@ -5,6 +5,7 @@ export enum PayloadAttestationErrorCode {
5
5
  NOT_CURRENT_SLOT = "PAYLOAD_ATTESTATION_ERROR_NOT_CURRENT_SLOT",
6
6
  PAYLOAD_ATTESTATION_ALREADY_KNOWN = "PAYLOAD_ATTESTATION_ERROR_PAYLOAD_ATTESTATION_ALREADY_KNOWN",
7
7
  UNKNOWN_BLOCK_ROOT = "PAYLOAD_ATTESTATION_ERROR_UNKNOWN_BLOCK_ROOT",
8
+ INVALID_BLOCK_SLOT = "PAYLOAD_ATTESTATION_ERROR_INVALID_BLOCK_SLOT",
8
9
  INVALID_BLOCK = "PAYLOAD_ATTESTATION_ERROR_INVALID_BLOCK",
9
10
  INVALID_ATTESTER = "PAYLOAD_ATTESTATION_ERROR_INVALID_ATTESTER",
10
11
  INVALID_SIGNATURE = "PAYLOAD_ATTESTATION_ERROR_INVALID_SIGNATURE",
@@ -18,6 +19,7 @@ export type PayloadAttestationErrorType =
18
19
  blockRoot: RootHex;
19
20
  }
20
21
  | {code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT; blockRoot: RootHex}
22
+ | {code: PayloadAttestationErrorCode.INVALID_BLOCK_SLOT; blockRoot: RootHex; blockSlot: Slot; slot: Slot}
21
23
  | {code: PayloadAttestationErrorCode.INVALID_BLOCK; blockRoot: RootHex}
22
24
  | {code: PayloadAttestationErrorCode.INVALID_ATTESTER; attesterIndex: ValidatorIndex}
23
25
  | {code: PayloadAttestationErrorCode.INVALID_SIGNATURE};
@@ -2,6 +2,7 @@ import {ChainForkConfig} from "@lodestar/config";
2
2
  import {
3
3
  ExecutionStatus,
4
4
  ForkChoice,
5
+ ForkChoiceStateGetter,
5
6
  ForkChoiceStore,
6
7
  JustifiedBalancesGetter,
7
8
  PayloadStatus,
@@ -45,6 +46,7 @@ export function initializeForkChoice(
45
46
  isFinalizedState: boolean,
46
47
  opts: ForkChoiceOpts,
47
48
  justifiedBalancesGetter: JustifiedBalancesGetter,
49
+ stateGetter: ForkChoiceStateGetter,
48
50
  metrics: Metrics | null,
49
51
  logger?: Logger
50
52
  ): ForkChoice {
@@ -56,6 +58,7 @@ export function initializeForkChoice(
56
58
  state,
57
59
  opts,
58
60
  justifiedBalancesGetter,
61
+ stateGetter,
59
62
  metrics,
60
63
  logger
61
64
  )
@@ -66,6 +69,7 @@ export function initializeForkChoice(
66
69
  state,
67
70
  opts,
68
71
  justifiedBalancesGetter,
72
+ stateGetter,
69
73
  metrics,
70
74
  logger
71
75
  );
@@ -81,6 +85,7 @@ export function initializeForkChoiceFromFinalizedState(
81
85
  state: IBeaconStateView,
82
86
  opts: ForkChoiceOpts,
83
87
  justifiedBalancesGetter: JustifiedBalancesGetter,
88
+ stateGetter: ForkChoiceStateGetter,
84
89
  metrics: Metrics | null,
85
90
  logger?: Logger
86
91
  ): ForkChoice {
@@ -112,6 +117,7 @@ export function initializeForkChoiceFromFinalizedState(
112
117
  finalizedCheckpoint,
113
118
  justifiedBalances,
114
119
  justifiedBalancesGetter,
120
+ stateGetter,
115
121
  {
116
122
  onJustified: (cp) => emitter.emit(ChainEvent.forkChoiceJustified, cp),
117
123
  onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp),
@@ -172,6 +178,7 @@ export function initializeForkChoiceFromUnfinalizedState(
172
178
  unfinalizedState: IBeaconStateView,
173
179
  opts: ForkChoiceOpts,
174
180
  justifiedBalancesGetter: JustifiedBalancesGetter,
181
+ stateGetter: ForkChoiceStateGetter,
175
182
  metrics: Metrics | null,
176
183
  logger?: Logger
177
184
  ): ForkChoice {
@@ -203,6 +210,7 @@ export function initializeForkChoiceFromUnfinalizedState(
203
210
  finalizedCheckpoint,
204
211
  justifiedBalances,
205
212
  justifiedBalancesGetter,
213
+ stateGetter,
206
214
  {
207
215
  onJustified: (cp) => emitter.emit(ChainEvent.forkChoiceJustified, cp),
208
216
  onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp),
@@ -12,13 +12,13 @@ type BlockRootHex = string;
12
12
  type BlockHashHex = string;
13
13
 
14
14
  /**
15
- * Store the best execution payload bid per slot / (parent block root, parent block hash).
15
+ * Store the best signed execution payload bid per slot / (parent block root, parent block hash).
16
16
  */
17
17
  export class ExecutionPayloadBidPool {
18
18
  private readonly bidByParentHashByParentRootBySlot = new MapDef<
19
19
  Slot,
20
- MapDef<BlockRootHex, Map<BlockHashHex, gloas.ExecutionPayloadBid>>
21
- >(() => new MapDef<BlockRootHex, Map<BlockHashHex, gloas.ExecutionPayloadBid>>(() => new Map()));
20
+ MapDef<BlockRootHex, Map<BlockHashHex, gloas.SignedExecutionPayloadBid>>
21
+ >(() => new MapDef<BlockRootHex, Map<BlockHashHex, gloas.SignedExecutionPayloadBid>>(() => new Map()));
22
22
  private lowestPermissibleSlot = 0;
23
23
 
24
24
  get size(): number {
@@ -31,8 +31,8 @@ export class ExecutionPayloadBidPool {
31
31
  return count;
32
32
  }
33
33
 
34
- add(bid: gloas.ExecutionPayloadBid): InsertOutcome {
35
- const {slot, parentBlockRoot, parentBlockHash, value} = bid;
34
+ add(bid: gloas.SignedExecutionPayloadBid): InsertOutcome {
35
+ const {slot, parentBlockRoot, parentBlockHash, value} = bid.message;
36
36
  const lowestPermissibleSlot = this.lowestPermissibleSlot;
37
37
 
38
38
  if (slot < lowestPermissibleSlot) {
@@ -45,7 +45,7 @@ export class ExecutionPayloadBidPool {
45
45
  const existing = bidByParentHash.get(parentHashHex);
46
46
 
47
47
  if (existing) {
48
- const existingValue = existing.value;
48
+ const existingValue = existing.message.value;
49
49
  const newValue = value;
50
50
  if (newValue > existingValue) {
51
51
  bidByParentHash.set(parentHashHex, bid);
@@ -59,14 +59,15 @@ export class ExecutionPayloadBidPool {
59
59
  }
60
60
 
61
61
  /**
62
- * Return the highest-value bid matching slot, parent block hash, and parent block root.
62
+ * Return the highest-value signed bid matching slot, parent block hash, and parent block root.
63
63
  * Used for gossip validation and block production.
64
64
  */
65
65
  getBestBid(
66
66
  slot: Slot,
67
- parentBlockHash: BlockHashHex,
67
+ parentBlockHash: BlockHashHex | null,
68
68
  parentBlockRoot: BlockRootHex
69
- ): gloas.ExecutionPayloadBid | null {
69
+ ): gloas.SignedExecutionPayloadBid | null {
70
+ if (parentBlockHash === null) return null;
70
71
  const bidByParentHash = this.bidByParentHashByParentRootBySlot.get(slot)?.get(parentBlockRoot);
71
72
  return bidByParentHash?.get(parentBlockHash) ?? null;
72
73
  }
@@ -105,6 +105,7 @@ export const defaultChainOptions: IChainOptions = {
105
105
  proposerBoost: true,
106
106
  proposerBoostReorg: true,
107
107
  computeUnrealized: true,
108
+ fastConfirmation: false,
108
109
  suggestedFeeRecipient: defaultValidatorOptions.suggestedFeeRecipient,
109
110
  serveHistoricalState: false,
110
111
  assertCorrectProgressiveBalances: false,
@@ -169,7 +169,8 @@ export class PrepareNextSlotScheduler {
169
169
  // Apply parent payload once here as it's reused by EL prep and SSE emit below
170
170
  let stateAfterParentPayload: IBeaconStateViewBellatrix = updatedPrepareState;
171
171
  if (isStatePostGloas(updatedPrepareState)) {
172
- if (this.chain.forkChoice.shouldExtendPayload(updatedHead.blockRoot)) {
172
+ // Spec: should_build_on_full(store, head) — see produceBlockBody.ts for context.
173
+ if (this.chain.forkChoice.shouldBuildOnFull(updatedHead, prepareSlot)) {
173
174
  parentBlockHash = updatedPrepareState.latestExecutionPayloadBid.blockHash;
174
175
  // Skip applying parent payload unless we're proposing the next slot or have to emit payload_attributes events
175
176
  if (feeRecipient !== undefined || this.chain.opts.emitPayloadAttributes === true) {
@@ -50,7 +50,7 @@ import {
50
50
  gloas,
51
51
  ssz,
52
52
  } from "@lodestar/types";
53
- import {Logger, byteArrayEquals, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
53
+ import {GWEI_TO_WEI, Logger, byteArrayEquals, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
54
54
  import {ZERO_HASH_HEX} from "../../constants/index.js";
55
55
  import {numToQuantity} from "../../execution/engine/utils.js";
56
56
  import {IExecutionBuilder, IExecutionEngine, PayloadAttributes, PayloadId} from "../../execution/index.js";
@@ -91,6 +91,8 @@ export type BlockAttributes = {
91
91
  slot: Slot;
92
92
  parentBlock: ProtoBlock;
93
93
  feeRecipient?: string;
94
+ /** When provided, build block with this builder bid instead of a self-build bid */
95
+ builderBid?: gloas.SignedExecutionPayloadBid;
94
96
  };
95
97
 
96
98
  export enum BlockType {
@@ -150,6 +152,28 @@ export type ProduceResult =
150
152
  | ProduceFullPhase0
151
153
  | ProduceBlinded;
152
154
 
155
+ /**
156
+ * Drop voluntary exits that `parent_execution_requests` have invalidated (e.g. a withdrawal
157
+ * request initiating an exit on the same validator). Op pool selected against the unapplied
158
+ * state, so re-validate against the post-apply state to avoid producing an invalid block.
159
+ *
160
+ * `getStateAfterParentPayload` is a thunk so the post-apply state is only materialized when
161
+ * actually needed (i.e. when extending the parent payload and there are exits to filter).
162
+ */
163
+ function maybeFilterInvalidatedVoluntaryExits(
164
+ commonBlockBody: CommonBlockBody,
165
+ isExtendingPayload: boolean,
166
+ getStateAfterParentPayload: () => IBeaconStateViewBellatrix
167
+ ): CommonBlockBody["voluntaryExits"] {
168
+ if (!isExtendingPayload || commonBlockBody.voluntaryExits.length === 0) {
169
+ return commonBlockBody.voluntaryExits;
170
+ }
171
+ const state = getStateAfterParentPayload();
172
+ return commonBlockBody.voluntaryExits.filter((signedVoluntaryExit) =>
173
+ state.isValidVoluntaryExit(signedVoluntaryExit, false)
174
+ );
175
+ }
176
+
153
177
  export async function produceBlockBody<T extends BlockType>(
154
178
  this: BeaconChain,
155
179
  blockType: T,
@@ -172,6 +196,7 @@ export async function produceBlockBody<T extends BlockType>(
172
196
  proposerIndex,
173
197
  proposerPubKey,
174
198
  commonBlockBodyPromise,
199
+ builderBid,
175
200
  } = blockAttr;
176
201
  let executionPayloadValue: Wei;
177
202
  let blockBody: AssembledBodyType<T>;
@@ -192,7 +217,43 @@ export async function produceBlockBody<T extends BlockType>(
192
217
  };
193
218
  this.logger.verbose("Producing beacon block body", logMeta);
194
219
 
195
- if (isForkPostGloas(fork)) {
220
+ if (builderBid !== undefined) {
221
+ if (!isStatePostGloas(currentState)) {
222
+ throw new Error("Expected Gloas state for builder bid block production");
223
+ }
224
+
225
+ const isExtendingPayload = byteArrayEquals(
226
+ builderBid.message.parentBlockHash,
227
+ currentState.latestExecutionPayloadBid.blockHash
228
+ );
229
+ const parentExecutionRequests = isExtendingPayload
230
+ ? await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot)
231
+ : ssz.electra.ExecutionRequests.defaultValue();
232
+ executionPayloadValue = BigInt(builderBid.message.value) * GWEI_TO_WEI;
233
+
234
+ const commonBlockBody = await commonBlockBodyPromise;
235
+ const gloasBody = Object.assign({}, commonBlockBody) as gloas.BeaconBlockBody;
236
+ gloasBody.signedExecutionPayloadBid = builderBid;
237
+ gloasBody.payloadAttestations = this.payloadAttestationPool.getPayloadAttestationsForBlock(
238
+ parentBlock.blockRoot,
239
+ blockSlot - 1
240
+ );
241
+ gloasBody.parentExecutionRequests = parentExecutionRequests;
242
+ gloasBody.voluntaryExits = maybeFilterInvalidatedVoluntaryExits(commonBlockBody, isExtendingPayload, () =>
243
+ currentState.withParentPayloadApplied(parentExecutionRequests)
244
+ );
245
+ blockBody = gloasBody as AssembledBodyType<T>;
246
+
247
+ this.logger.verbose("Produced block with builder bid", {
248
+ slot: blockSlot,
249
+ builderIndex: builderBid.message.builderIndex,
250
+ bidValue: builderBid.message.value,
251
+ parentBlockHash: toRootHex(builderBid.message.parentBlockHash),
252
+ parentBlockRoot: toRootHex(builderBid.message.parentBlockRoot),
253
+ blockHash: toRootHex(builderBid.message.blockHash),
254
+ isExtendingPayload,
255
+ });
256
+ } else if (isForkPostGloas(fork)) {
196
257
  if (!isStatePostGloas(currentState)) {
197
258
  throw new Error("Expected Gloas state for Gloas block production");
198
259
  }
@@ -209,19 +270,16 @@ export async function produceBlockBody<T extends BlockType>(
209
270
 
210
271
  const endExecutionPayload = this.metrics?.executionBlockProductionTimeSteps.startTimer();
211
272
 
212
- this.logger.verbose("Preparing execution payload from engine", {
213
- slot: blockSlot,
214
- parentBlockRoot: toRootHex(parentBlockRoot),
215
- feeRecipient,
216
- });
217
-
218
273
  // Get execution payload from EL
219
274
  let parentBlockHash: Bytes32;
220
275
  let parentExecutionRequests: electra.ExecutionRequests;
221
276
  // Apply parent payload once here as it's reused by EL prep and voluntary exit filtering below
222
277
  let stateAfterParentPayload: IBeaconStateViewBellatrix = currentState;
223
- const isExtendingPayload = this.forkChoice.shouldExtendPayload(toRootHex(parentBlockRoot));
224
- if (isExtendingPayload) {
278
+ // Spec: should_build_on_full(store, head). `parentBlock` is the proposer's head
279
+ // (set by chain.getProposerHead(slot)). Returns false when the PTC majority signalled
280
+ // the blob data is not available or the payload was not timely, forcing a build on EMPTY (reorg).
281
+ const isBuildingOnFull = this.forkChoice.shouldBuildOnFull(parentBlock, blockSlot);
282
+ if (isBuildingOnFull) {
225
283
  parentBlockHash = currentState.latestExecutionPayloadBid.blockHash;
226
284
  parentExecutionRequests = await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot);
227
285
  stateAfterParentPayload = currentState.withParentPayloadApplied(parentExecutionRequests);
@@ -244,6 +302,16 @@ export async function produceBlockBody<T extends BlockType>(
244
302
  const {prepType, payloadId} = prepareRes;
245
303
  Object.assign(logMeta, {executionPayloadPrepType: prepType});
246
304
 
305
+ this.logger.verbose("Prepared execution payload from engine", {
306
+ slot: blockSlot,
307
+ parentBlockRoot: toRootHex(parentBlockRoot),
308
+ parentBlockHash: toRootHex(parentBlockHash),
309
+ feeRecipient,
310
+ prepType,
311
+ payloadId,
312
+ isBuildingOnFull,
313
+ });
314
+
247
315
  if (prepType !== PayloadPreparationType.Cached) {
248
316
  await sleep(PAYLOAD_GENERATION_TIME_MS);
249
317
  }
@@ -297,14 +365,11 @@ export async function produceBlockBody<T extends BlockType>(
297
365
  blockSlot - 1
298
366
  );
299
367
  gloasBody.parentExecutionRequests = parentExecutionRequests;
300
- // Drop voluntary exits that parent_execution_requests have invalidated (e.g. a withdrawal
301
- // request initiating an exit on the same validator). Op pool selected against the unapplied
302
- // state, so re-validate against the post-apply state to avoid producing an invalid block.
303
- if (isExtendingPayload && commonBlockBody.voluntaryExits.length > 0) {
304
- gloasBody.voluntaryExits = commonBlockBody.voluntaryExits.filter((signedVoluntaryExit) =>
305
- stateAfterParentPayload.isValidVoluntaryExit(signedVoluntaryExit, false)
306
- );
307
- }
368
+ gloasBody.voluntaryExits = maybeFilterInvalidatedVoluntaryExits(
369
+ commonBlockBody,
370
+ isBuildingOnFull,
371
+ () => stateAfterParentPayload
372
+ );
308
373
  blockBody = gloasBody as AssembledBodyType<T>;
309
374
 
310
375
  // Store execution payload data required to construct execution payload envelope later
@@ -17,10 +17,12 @@ export enum RegenCaller {
17
17
  predictProposerHead = "predictProposerHead",
18
18
  produceAttestationData = "produceAttestationData",
19
19
  processBlocksInEpoch = "processBlocksInEpoch",
20
+ importExecutionPayload = "importExecutionPayload",
20
21
  validateGossipAggregateAndProof = "validateGossipAggregateAndProof",
21
22
  validateGossipAttestation = "validateGossipAttestation",
22
23
  validateGossipVoluntaryExit = "validateGossipVoluntaryExit",
23
24
  validateGossipExecutionPayloadBid = "validateGossipExecutionPayloadBid",
25
+ validateGossipPayloadAttestationMessage = "validateGossipPayloadAttestationMessage",
24
26
  validateGossipProposerPreferences = "validateGossipProposerPreferences",
25
27
  onForkChoiceFinalized = "onForkChoiceFinalized",
26
28
  restApi = "restApi",
@@ -45,7 +47,6 @@ export interface IStateRegenerator extends IStateRegeneratorInternal {
45
47
  getCheckpointStateSync(cp: CheckpointHex): IBeaconStateView | null;
46
48
  getClosestHeadState(head: ProtoBlock): IBeaconStateView | null;
47
49
  pruneOnCheckpoint(finalizedEpoch: Epoch, justifiedEpoch: Epoch, headStateRoot: RootHex): void;
48
- pruneOnFinalized(finalizedEpoch: Epoch): void;
49
50
  processState(blockRootHex: RootHex, postState: IBeaconStateView): void;
50
51
  addCheckpointState(cp: phase0.Checkpoint, item: IBeaconStateView): void;
51
52
  updateHeadState(newHead: ProtoBlock, maybeHeadState: IBeaconStateView): void;