@lodestar/beacon-node 1.44.0-dev.552cdce8d0 → 1.44.0-dev.5a822af737

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 (220) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +50 -29
  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 -26
  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 +121 -69
  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.d.ts.map +1 -1
  28. package/lib/chain/blocks/importExecutionPayload.js +5 -3
  29. package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
  30. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
  31. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
  32. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
  33. package/lib/chain/chain.d.ts +1 -1
  34. package/lib/chain/chain.d.ts.map +1 -1
  35. package/lib/chain/chain.js +10 -2
  36. package/lib/chain/chain.js.map +1 -1
  37. package/lib/chain/emitter.d.ts +2 -1
  38. package/lib/chain/emitter.d.ts.map +1 -1
  39. package/lib/chain/emitter.js.map +1 -1
  40. package/lib/chain/errors/blockError.d.ts +0 -7
  41. package/lib/chain/errors/blockError.d.ts.map +1 -1
  42. package/lib/chain/errors/blockError.js +0 -3
  43. package/lib/chain/errors/blockError.js.map +1 -1
  44. package/lib/chain/errors/executionPayloadBid.d.ts +17 -1
  45. package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
  46. package/lib/chain/errors/executionPayloadBid.js +3 -0
  47. package/lib/chain/errors/executionPayloadBid.js.map +1 -1
  48. package/lib/chain/errors/payloadAttestation.d.ts +6 -0
  49. package/lib/chain/errors/payloadAttestation.d.ts.map +1 -1
  50. package/lib/chain/errors/payloadAttestation.js +1 -0
  51. package/lib/chain/errors/payloadAttestation.js.map +1 -1
  52. package/lib/chain/forkChoice/index.d.ts +4 -4
  53. package/lib/chain/forkChoice/index.d.ts.map +1 -1
  54. package/lib/chain/forkChoice/index.js +24 -11
  55. package/lib/chain/forkChoice/index.js.map +1 -1
  56. package/lib/chain/opPools/executionPayloadBidPool.d.ts +4 -4
  57. package/lib/chain/opPools/executionPayloadBidPool.d.ts.map +1 -1
  58. package/lib/chain/opPools/executionPayloadBidPool.js +6 -4
  59. package/lib/chain/opPools/executionPayloadBidPool.js.map +1 -1
  60. package/lib/chain/options.d.ts.map +1 -1
  61. package/lib/chain/options.js +1 -0
  62. package/lib/chain/options.js.map +1 -1
  63. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  64. package/lib/chain/prepareNextSlot.js +2 -1
  65. package/lib/chain/prepareNextSlot.js.map +1 -1
  66. package/lib/chain/produceBlock/produceBlockBody.d.ts +7 -1
  67. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  68. package/lib/chain/produceBlock/produceBlockBody.js +108 -19
  69. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  70. package/lib/chain/regen/interface.d.ts +2 -1
  71. package/lib/chain/regen/interface.d.ts.map +1 -1
  72. package/lib/chain/regen/interface.js +2 -0
  73. package/lib/chain/regen/interface.js.map +1 -1
  74. package/lib/chain/regen/queued.d.ts +0 -1
  75. package/lib/chain/regen/queued.d.ts.map +1 -1
  76. package/lib/chain/regen/queued.js +0 -4
  77. package/lib/chain/regen/queued.js.map +1 -1
  78. package/lib/chain/stateCache/fifoBlockStateCache.d.ts +0 -5
  79. package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
  80. package/lib/chain/stateCache/fifoBlockStateCache.js +0 -5
  81. package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
  82. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +1 -4
  83. package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
  84. package/lib/chain/stateCache/persistentCheckpointsCache.js +5 -2
  85. package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
  86. package/lib/chain/stateCache/types.d.ts +0 -2
  87. package/lib/chain/stateCache/types.d.ts.map +1 -1
  88. package/lib/chain/stateCache/types.js.map +1 -1
  89. package/lib/chain/validation/block.d.ts +5 -1
  90. package/lib/chain/validation/block.d.ts.map +1 -1
  91. package/lib/chain/validation/block.js +4 -14
  92. package/lib/chain/validation/block.js.map +1 -1
  93. package/lib/chain/validation/executionPayloadBid.d.ts +7 -3
  94. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  95. package/lib/chain/validation/executionPayloadBid.js +70 -16
  96. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  97. package/lib/chain/validation/executionPayloadEnvelope.js +0 -2
  98. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  99. package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
  100. package/lib/chain/validation/payloadAttestationMessage.js +24 -4
  101. package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
  102. package/lib/chain/validatorMonitor.d.ts +1 -0
  103. package/lib/chain/validatorMonitor.d.ts.map +1 -1
  104. package/lib/chain/validatorMonitor.js +16 -0
  105. package/lib/chain/validatorMonitor.js.map +1 -1
  106. package/lib/execution/builder/index.d.ts +1 -2
  107. package/lib/execution/builder/index.d.ts.map +1 -1
  108. package/lib/execution/builder/index.js +0 -1
  109. package/lib/execution/builder/index.js.map +1 -1
  110. package/lib/execution/engine/interface.d.ts +1 -0
  111. package/lib/execution/engine/interface.d.ts.map +1 -1
  112. package/lib/execution/engine/types.d.ts +2 -0
  113. package/lib/execution/engine/types.d.ts.map +1 -1
  114. package/lib/execution/engine/types.js +2 -0
  115. package/lib/execution/engine/types.js.map +1 -1
  116. package/lib/metrics/metrics/lodestar.d.ts +5 -1
  117. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  118. package/lib/metrics/metrics/lodestar.js +14 -3
  119. package/lib/metrics/metrics/lodestar.js.map +1 -1
  120. package/lib/network/gossip/topic.d.ts +2 -2
  121. package/lib/network/interface.d.ts +1 -0
  122. package/lib/network/interface.d.ts.map +1 -1
  123. package/lib/network/network.d.ts +1 -0
  124. package/lib/network/network.d.ts.map +1 -1
  125. package/lib/network/network.js +5 -0
  126. package/lib/network/network.js.map +1 -1
  127. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  128. package/lib/network/processor/gossipHandlers.js +21 -6
  129. package/lib/network/processor/gossipHandlers.js.map +1 -1
  130. package/lib/network/processor/index.d.ts +2 -2
  131. package/lib/network/processor/index.d.ts.map +1 -1
  132. package/lib/network/processor/index.js +25 -23
  133. package/lib/network/processor/index.js.map +1 -1
  134. package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
  135. package/lib/network/reqresp/handlers/beaconBlocksByRange.js +9 -5
  136. package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
  137. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
  138. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +13 -3
  139. package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
  140. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +1 -1
  141. package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
  142. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts +2 -1
  143. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
  144. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +16 -6
  145. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
  146. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts +2 -1
  147. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
  148. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +15 -1
  149. package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
  150. package/lib/network/reqresp/handlers/index.js +4 -4
  151. package/lib/network/reqresp/handlers/index.js.map +1 -1
  152. package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
  153. package/lib/network/reqresp/utils/dataColumnResponseValidation.js +22 -3
  154. package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
  155. package/lib/sync/types.d.ts +9 -1
  156. package/lib/sync/types.d.ts.map +1 -1
  157. package/lib/sync/types.js +9 -2
  158. package/lib/sync/types.js.map +1 -1
  159. package/lib/sync/unknownBlock.d.ts.map +1 -1
  160. package/lib/sync/unknownBlock.js +64 -30
  161. package/lib/sync/unknownBlock.js.map +1 -1
  162. package/lib/util/dataColumns.d.ts.map +1 -1
  163. package/lib/util/dataColumns.js +16 -11
  164. package/lib/util/dataColumns.js.map +1 -1
  165. package/lib/util/sszBytes.js +1 -1
  166. package/package.json +15 -17
  167. package/src/api/impl/beacon/blocks/index.ts +60 -38
  168. package/src/api/impl/beacon/pool/index.ts +4 -42
  169. package/src/api/impl/config/constants.ts +2 -0
  170. package/src/api/impl/debug/index.ts +73 -12
  171. package/src/api/impl/lodestar/index.ts +30 -0
  172. package/src/api/impl/validator/index.ts +141 -82
  173. package/src/chain/archiveStore/archiveStore.ts +0 -5
  174. package/src/chain/blocks/importBlock.ts +10 -2
  175. package/src/chain/blocks/importExecutionPayload.ts +8 -2
  176. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +2 -4
  177. package/src/chain/chain.ts +12 -1
  178. package/src/chain/emitter.ts +3 -2
  179. package/src/chain/errors/blockError.ts +0 -4
  180. package/src/chain/errors/executionPayloadBid.ts +13 -1
  181. package/src/chain/errors/payloadAttestation.ts +2 -0
  182. package/src/chain/forkChoice/index.ts +27 -4
  183. package/src/chain/opPools/executionPayloadBidPool.ts +10 -9
  184. package/src/chain/options.ts +1 -0
  185. package/src/chain/prepareNextSlot.ts +2 -1
  186. package/src/chain/produceBlock/produceBlockBody.ts +159 -26
  187. package/src/chain/regen/interface.ts +2 -1
  188. package/src/chain/regen/queued.ts +0 -5
  189. package/src/chain/stateCache/fifoBlockStateCache.ts +0 -6
  190. package/src/chain/stateCache/persistentCheckpointsCache.ts +6 -2
  191. package/src/chain/stateCache/types.ts +0 -2
  192. package/src/chain/validation/block.ts +12 -16
  193. package/src/chain/validation/executionPayloadBid.ts +78 -19
  194. package/src/chain/validation/executionPayloadEnvelope.ts +0 -2
  195. package/src/chain/validation/payloadAttestationMessage.ts +26 -4
  196. package/src/chain/validatorMonitor.ts +18 -0
  197. package/src/execution/builder/index.ts +1 -4
  198. package/src/execution/engine/interface.ts +1 -0
  199. package/src/execution/engine/types.ts +4 -0
  200. package/src/metrics/metrics/lodestar.ts +15 -3
  201. package/src/network/interface.ts +1 -0
  202. package/src/network/network.ts +11 -0
  203. package/src/network/processor/gossipHandlers.ts +26 -6
  204. package/src/network/processor/index.ts +27 -27
  205. package/src/network/reqresp/handlers/beaconBlocksByRange.ts +12 -5
  206. package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +17 -3
  207. package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +1 -1
  208. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +22 -6
  209. package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +20 -1
  210. package/src/network/reqresp/handlers/index.ts +4 -4
  211. package/src/network/reqresp/utils/dataColumnResponseValidation.ts +21 -3
  212. package/src/sync/types.ts +11 -2
  213. package/src/sync/unknownBlock.ts +70 -31
  214. package/src/util/dataColumns.ts +17 -12
  215. package/src/util/sszBytes.ts +1 -1
  216. package/lib/execution/builder/utils.d.ts +0 -5
  217. package/lib/execution/builder/utils.d.ts.map +0 -1
  218. package/lib/execution/builder/utils.js +0 -17
  219. package/lib/execution/builder/utils.js.map +0 -1
  220. package/src/execution/builder/utils.ts +0 -19
@@ -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,15 +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
- const payloadPresent = blockIsForSlot && (payloadInput?.hasPayloadEnvelope() ?? false);
1071
- const blobDataAvailable = blockIsForSlot && (payloadInput?.hasAllData() ?? false);
1120
+ // Spec: set payload_present only if the envelope was seen before get_payload_due_ms()
1121
+ // into the slot. Use the envelope's own arrival time (getPayloadEnvelopeSource), not
1122
+ // the input's creation time.
1123
+ const payloadDueSec = config.getPayloadDueMs() / 1000;
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
+ });
1072
1141
 
1073
1142
  return {
1074
1143
  data: {
@@ -1138,7 +1207,10 @@ export function getValidatorApi(
1138
1207
  const isPostFulu = isForkPostFulu(config.getForkName(startSlot));
1139
1208
  const maxFutureEpoch = isPostFulu && nearNextEpoch && opts?.v2 ? nextEpoch + 1 : nextEpoch;
1140
1209
  if (currentEpoch >= 0 && epoch > maxFutureEpoch) {
1141
- throw new ApiError(400, `Requested epoch ${epoch} must not be more than one epoch in the future`);
1210
+ throw new ApiError(
1211
+ 400,
1212
+ `Requested epoch ${epoch} must not be more than ${maxFutureEpoch}, currentEpoch=${currentEpoch}, v2=${opts?.v2 ?? false}`
1213
+ );
1142
1214
  }
1143
1215
 
1144
1216
  const head = chain.forkChoice.getHead();
@@ -1222,17 +1294,35 @@ export function getValidatorApi(
1222
1294
  duties.push({slot: startSlot + i, validatorIndex: indexes[i], pubkey: pubkeys[i]});
1223
1295
  }
1224
1296
 
1225
- // Returns `null` on the one-off scenario where the genesis block decides its own shuffling.
1226
- // It should be set to the latest block applied to `self` or the genesis block root.
1227
- const dependentRoot =
1228
- // In v2 the dependent root is different after fulu due to deterministic proposer lookahead
1229
- proposerShufflingDecisionRoot(opts?.v2 ? config.getForkName(startSlot) : ForkName.phase0, state, epoch) ||
1230
- (await getGenesisBlockRoot(state));
1297
+ // In v2 the dependent root is different after fulu due to deterministic proposer lookahead
1298
+ let dependentRoot = proposerShufflingDecisionRoot(
1299
+ opts?.v2 ? config.getForkName(startSlot) : ForkName.phase0,
1300
+ state,
1301
+ epoch
1302
+ );
1303
+ const logCtx = {
1304
+ epoch,
1305
+ stateSlot: state.slot,
1306
+ stateEpoch: state.epoch,
1307
+ v2: opts?.v2 ?? false,
1308
+ };
1309
+ if (dependentRoot === null) {
1310
+ // fallback to get_proposer_duties() v1, also in lodestar v1.43
1311
+ logger.verbose("Proposer duties decision root not in state, falling back to state epoch", logCtx);
1312
+ dependentRoot = proposerShufflingDecisionRoot(ForkName.phase0, state, state.epoch);
1313
+ }
1314
+ if (dependentRoot === null) {
1315
+ logger.verbose("Proposer duties decision root not in state, falling back to genesis block root", logCtx);
1316
+ dependentRoot = await getGenesisBlockRoot(state);
1317
+ }
1318
+
1319
+ const dependentRootHex = toRootHex(dependentRoot);
1320
+ logger.verbose("Computed proposer duties decision root", {...logCtx, dependentRoot: dependentRootHex});
1231
1321
 
1232
1322
  return {
1233
1323
  data: duties,
1234
1324
  meta: {
1235
- dependentRoot: toRootHex(dependentRoot),
1325
+ dependentRoot: dependentRootHex,
1236
1326
  executionOptimistic: isOptimisticBlock(head),
1237
1327
  },
1238
1328
  };
@@ -1411,33 +1501,6 @@ export function getValidatorApi(
1411
1501
  };
1412
1502
  },
1413
1503
 
1414
- async getAggregatedAttestation({attestationDataRoot, slot}) {
1415
- notWhileSyncing();
1416
-
1417
- await waitForSlot(slot); // Must never request for a future slot > currentSlot
1418
-
1419
- const dataRootHex = toRootHex(attestationDataRoot);
1420
- const aggregate = chain.attestationPool.getAggregate(slot, dataRootHex, null);
1421
- const fork = chain.config.getForkName(slot);
1422
-
1423
- if (isForkPostElectra(fork)) {
1424
- throw new ApiError(
1425
- 400,
1426
- `Use getAggregatedAttestationV2 to retrieve aggregated attestations for post-electra fork=${fork}`
1427
- );
1428
- }
1429
-
1430
- if (!aggregate) {
1431
- throw new ApiError(404, `No aggregated attestation for slot=${slot}, dataRoot=${dataRootHex}`);
1432
- }
1433
-
1434
- metrics?.production.producedAggregateParticipants.observe(aggregate.aggregationBits.getTrueBitIndexes().length);
1435
-
1436
- return {
1437
- data: aggregate,
1438
- };
1439
- },
1440
-
1441
1504
  async getAggregatedAttestationV2({attestationDataRoot, slot, committeeIndex}) {
1442
1505
  notWhileSyncing();
1443
1506
 
@@ -1461,10 +1524,6 @@ export function getValidatorApi(
1461
1524
  };
1462
1525
  },
1463
1526
 
1464
- async publishAggregateAndProofs({signedAggregateAndProofs}) {
1465
- await this.publishAggregateAndProofsV2({signedAggregateAndProofs});
1466
- },
1467
-
1468
1527
  async publishAggregateAndProofsV2({signedAggregateAndProofs}) {
1469
1528
  notWhileSyncing();
1470
1529
 
@@ -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)
@@ -237,6 +237,7 @@ export async function importExecutionPayload(
237
237
  blockRootHex,
238
238
  blockHashHex,
239
239
  envelope.payload.blockNumber,
240
+ envelope.payload.gasLimit,
240
241
  execStatus,
241
242
  dataAvailabilityStatus
242
243
  );
@@ -254,7 +255,11 @@ export async function importExecutionPayload(
254
255
  }
255
256
 
256
257
  // 8. Record metrics for payload envelope and column sources
257
- this.metrics?.importPayload.bySource.inc({source: payloadInput.getPayloadEnvelopeSource().source});
258
+ const delaySec = this.clock.secFromSlot(slot);
259
+ this.metrics?.importPayload.elapsedTimeTillImported.observe(
260
+ {source: payloadInput.getPayloadEnvelopeSource().source},
261
+ delaySec
262
+ );
258
263
  for (const {source} of payloadInput.getSampledColumnsWithSource()) {
259
264
  this.metrics?.importPayload.columnsBySource.inc({source});
260
265
  }
@@ -275,6 +280,7 @@ export async function importExecutionPayload(
275
280
  builderIndex: envelope.builderIndex,
276
281
  blockRoot: blockRootHex,
277
282
  blockHash: blockHashHex,
283
+ delaySec,
278
284
  });
279
285
  }
280
286
 
@@ -61,10 +61,8 @@ export function verifyExecutionPayloadEnvelope(
61
61
  `Prev randao mismatch between bid and payload bid=${toHex(bid.prevRandao)} payload=${toHex(payload.prevRandao)}`
62
62
  );
63
63
  }
64
- if (Number(bid.gasLimit) !== payload.gasLimit) {
65
- throw new Error(
66
- `Gas limit mismatch between payload and bid payload=${payload.gasLimit} bid=${Number(bid.gasLimit)}`
67
- );
64
+ if (bid.gasLimit !== payload.gasLimit) {
65
+ throw new Error(`Gas limit mismatch between payload and bid payload=${payload.gasLimit} bid=${bid.gasLimit}`);
68
66
  }
69
67
  if (!byteArrayEquals(bid.blockHash, payload.blockHash)) {
70
68
  throw new Error(
@@ -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
 
@@ -3,7 +3,7 @@ import {StrictEventEmitter} from "strict-event-emitter-types";
3
3
  import {routes} from "@lodestar/api";
4
4
  import {CheckpointWithHex} from "@lodestar/fork-choice";
5
5
  import {IBeaconStateView} from "@lodestar/state-transition";
6
- import {DataColumnSidecar, RootHex, deneb, phase0} from "@lodestar/types";
6
+ import {DataColumnSidecar, RootHex, Slot, deneb, phase0} from "@lodestar/types";
7
7
  import {PeerIdStr} from "../util/peerId.js";
8
8
  import {BlockInputSource, IBlockInput} from "./blocks/blockInput/types.js";
9
9
  import {PayloadEnvelopeInput} from "./blocks/payloadEnvelopeInput/payloadEnvelopeInput.js";
@@ -94,7 +94,8 @@ export type ChainEventData = {
94
94
  peer: PeerIdStr;
95
95
  source: BlockInputSource;
96
96
  };
97
- [ChainEvent.unknownEnvelopeBlockRoot]: {rootHex: RootHex; peer?: PeerIdStr; source: BlockInputSource};
97
+ // slot is the message slot, not necessarily the envelope's slot, but useful as a logging/prune hint
98
+ [ChainEvent.unknownEnvelopeBlockRoot]: {rootHex: RootHex; slot: Slot; peer?: PeerIdStr; source: BlockInputSource};
98
99
  };
99
100
 
100
101
  export type IChainEvents = ApiEvents & {
@@ -61,9 +61,6 @@ export enum BlockErrorCode {
61
61
  TRANSACTIONS_TOO_BIG = "BLOCK_ERROR_TRANSACTIONS_TOO_BIG",
62
62
  /** Execution engine is unavailable, syncing, or api call errored. Peers must not be downscored on this code */
63
63
  EXECUTION_ENGINE_ERROR = "BLOCK_ERROR_EXECUTION_ERROR",
64
- /** The attestation head block is too far behind the attestation slot, causing many skip slots.
65
- This is deemed a DoS risk */
66
- TOO_MANY_SKIPPED_SLOTS = "TOO_MANY_SKIPPED_SLOTS",
67
64
  /** The blobs are unavailable */
68
65
  DATA_UNAVAILABLE = "BLOCK_ERROR_DATA_UNAVAILABLE",
69
66
  /** Block contains too many kzg commitments */
@@ -89,7 +86,6 @@ export type BlockErrorType =
89
86
  | {code: BlockErrorCode.FUTURE_SLOT; blockSlot: Slot; currentSlot: Slot}
90
87
  | {code: BlockErrorCode.STATE_ROOT_MISMATCH}
91
88
  | {code: BlockErrorCode.GENESIS_BLOCK}
92
- | {code: BlockErrorCode.TOO_MANY_SKIPPED_SLOTS; parentSlot: Slot; blockSlot: Slot}
93
89
  | {code: BlockErrorCode.WOULD_REVERT_FINALIZED_SLOT; blockSlot: Slot; finalizedSlot: Slot}
94
90
  | {code: BlockErrorCode.ALREADY_KNOWN; root: RootHex}
95
91
  | {code: BlockErrorCode.REPEAT_PROPOSAL; proposerIndex: ValidatorIndex}
@@ -9,7 +9,10 @@ export enum ExecutionPayloadBidErrorCode {
9
9
  BID_TOO_HIGH = "EXECUTION_PAYLOAD_BID_ERROR_BID_TOO_HIGH",
10
10
  TOO_MANY_KZG_COMMITMENTS = "EXECUTION_PAYLOAD_BID_ERROR_TOO_MANY_KZG_COMMITMENTS",
11
11
  UNKNOWN_BLOCK_ROOT = "EXECUTION_PAYLOAD_BID_ERROR_UNKNOWN_BLOCK_ROOT",
12
+ UNKNOWN_PARENT_BLOCK_HASH = "EXECUTION_PAYLOAD_BID_ERROR_UNKNOWN_PARENT_BLOCK_HASH",
12
13
  INVALID_SLOT = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SLOT",
14
+ NOT_LATER_THAN_PARENT = "EXECUTION_PAYLOAD_BID_ERROR_NOT_LATER_THAN_PARENT",
15
+ INVALID_PREV_RANDAO = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_PREV_RANDAO",
13
16
  INVALID_SIGNATURE = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SIGNATURE",
14
17
  NO_MATCHING_PROPOSER_PREFERENCES = "EXECUTION_PAYLOAD_BID_ERROR_NO_MATCHING_PROPOSER_PREFERENCES",
15
18
  PROPOSER_PREFERENCES_FEE_RECIPIENT_MISMATCH = "EXECUTION_PAYLOAD_BID_ERROR_PROPOSER_PREFERENCES_FEE_RECIPIENT_MISMATCH",
@@ -38,7 +41,15 @@ export type ExecutionPayloadBidErrorType =
38
41
  commitmentLimit: number;
39
42
  }
40
43
  | {code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT; parentBlockRoot: RootHex}
44
+ | {code: ExecutionPayloadBidErrorCode.UNKNOWN_PARENT_BLOCK_HASH; parentBlockHash: RootHex}
41
45
  | {code: ExecutionPayloadBidErrorCode.INVALID_SLOT; builderIndex: BuilderIndex; slot: Slot}
46
+ | {code: ExecutionPayloadBidErrorCode.NOT_LATER_THAN_PARENT; parentSlot: Slot; slot: Slot}
47
+ | {
48
+ code: ExecutionPayloadBidErrorCode.INVALID_PREV_RANDAO;
49
+ builderIndex: BuilderIndex;
50
+ bidPrevRandao: string;
51
+ expectedPrevRandao: string;
52
+ }
42
53
  | {code: ExecutionPayloadBidErrorCode.INVALID_SIGNATURE; builderIndex: BuilderIndex; slot: Slot}
43
54
  | {
44
55
  code: ExecutionPayloadBidErrorCode.NO_MATCHING_PROPOSER_PREFERENCES;
@@ -56,7 +67,8 @@ export type ExecutionPayloadBidErrorType =
56
67
  code: ExecutionPayloadBidErrorCode.PROPOSER_PREFERENCES_GAS_LIMIT_MISMATCH;
57
68
  builderIndex: BuilderIndex;
58
69
  bidGasLimit: number;
59
- expectedGasLimit: number;
70
+ parentGasLimit: number;
71
+ targetGasLimit: number;
60
72
  };
61
73
 
62
74
  export class ExecutionPayloadBidError extends GossipActionError<ExecutionPayloadBidErrorType> {}
@@ -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};
@@ -1,7 +1,9 @@
1
+ import {routes} from "@lodestar/api";
1
2
  import {ChainForkConfig} from "@lodestar/config";
2
3
  import {
3
4
  ExecutionStatus,
4
5
  ForkChoice,
6
+ ForkChoiceStateGetter,
5
7
  ForkChoiceStore,
6
8
  JustifiedBalancesGetter,
7
9
  PayloadStatus,
@@ -45,6 +47,7 @@ export function initializeForkChoice(
45
47
  isFinalizedState: boolean,
46
48
  opts: ForkChoiceOpts,
47
49
  justifiedBalancesGetter: JustifiedBalancesGetter,
50
+ stateGetter: ForkChoiceStateGetter,
48
51
  metrics: Metrics | null,
49
52
  logger?: Logger
50
53
  ): ForkChoice {
@@ -56,6 +59,7 @@ export function initializeForkChoice(
56
59
  state,
57
60
  opts,
58
61
  justifiedBalancesGetter,
62
+ stateGetter,
59
63
  metrics,
60
64
  logger
61
65
  )
@@ -66,6 +70,7 @@ export function initializeForkChoice(
66
70
  state,
67
71
  opts,
68
72
  justifiedBalancesGetter,
73
+ stateGetter,
69
74
  metrics,
70
75
  logger
71
76
  );
@@ -81,6 +86,7 @@ export function initializeForkChoiceFromFinalizedState(
81
86
  state: IBeaconStateView,
82
87
  opts: ForkChoiceOpts,
83
88
  justifiedBalancesGetter: JustifiedBalancesGetter,
89
+ stateGetter: ForkChoiceStateGetter,
84
90
  metrics: Metrics | null,
85
91
  logger?: Logger
86
92
  ): ForkChoice {
@@ -112,9 +118,12 @@ export function initializeForkChoiceFromFinalizedState(
112
118
  finalizedCheckpoint,
113
119
  justifiedBalances,
114
120
  justifiedBalancesGetter,
121
+ stateGetter,
115
122
  {
116
123
  onJustified: (cp) => emitter.emit(ChainEvent.forkChoiceJustified, cp),
117
124
  onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp),
125
+ onFastConfirmation: ({block, slot, currentSlot}) =>
126
+ emitter.emit(routes.events.EventType.fastConfirmation, {block, slot, currentSlot}),
118
127
  }
119
128
  ),
120
129
 
@@ -140,9 +149,11 @@ export function initializeForkChoiceFromFinalizedState(
140
149
  executionPayloadBlockHash: isStatePostGloas(state)
141
150
  ? toRootHex(state.latestBlockHash)
142
151
  : toRootHex(state.latestExecutionPayloadHeader.blockHash),
143
- // TODO GLOAS: executionPayloadNumber is not tracked in BeaconState post-gloas (EIP-7732 removed
144
- // latestExecutionPayloadHeader). Using 0 as unavailable fallback until a solution is found.
152
+ // TODO GLOAS: executionPayloadNumber/GasLimit are not tracked in BeaconState post-gloas
153
+ // (EIP-7732 removed latestExecutionPayloadHeader). Using 0 as unavailable fallback
154
+ // see initializeForkChoiceFromUnfinalizedState for the same caveat on validation.
145
155
  executionPayloadNumber: isStatePostGloas(state) ? 0 : state.payloadBlockNumber,
156
+ executionPayloadGasLimit: isStatePostGloas(state) ? 0 : state.latestExecutionPayloadHeader.gasLimit,
146
157
  executionStatus: blockHeader.slot === GENESIS_SLOT ? ExecutionStatus.Valid : ExecutionStatus.Syncing,
147
158
  }
148
159
  : {executionPayloadBlockHash: null, executionStatus: ExecutionStatus.PreMerge}),
@@ -170,6 +181,7 @@ export function initializeForkChoiceFromUnfinalizedState(
170
181
  unfinalizedState: IBeaconStateView,
171
182
  opts: ForkChoiceOpts,
172
183
  justifiedBalancesGetter: JustifiedBalancesGetter,
184
+ stateGetter: ForkChoiceStateGetter,
173
185
  metrics: Metrics | null,
174
186
  logger?: Logger
175
187
  ): ForkChoice {
@@ -201,9 +213,12 @@ export function initializeForkChoiceFromUnfinalizedState(
201
213
  finalizedCheckpoint,
202
214
  justifiedBalances,
203
215
  justifiedBalancesGetter,
216
+ stateGetter,
204
217
  {
205
218
  onJustified: (cp) => emitter.emit(ChainEvent.forkChoiceJustified, cp),
206
219
  onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp),
220
+ onFastConfirmation: ({block, slot, currentSlot}) =>
221
+ emitter.emit(routes.events.EventType.fastConfirmation, {block, slot, currentSlot}),
207
222
  }
208
223
  );
209
224
 
@@ -232,9 +247,17 @@ export function initializeForkChoiceFromUnfinalizedState(
232
247
  executionPayloadBlockHash: isStatePostGloas(unfinalizedState)
233
248
  ? toRootHex(unfinalizedState.latestBlockHash)
234
249
  : toRootHex(unfinalizedState.latestExecutionPayloadHeader.blockHash),
235
- // TODO GLOAS: executionPayloadNumber is not tracked in BeaconState post-gloas (EIP-7732 removed
236
- // latestExecutionPayloadHeader). Using 0 as unavailable fallback until a solution is found.
250
+ // TODO GLOAS: executionPayloadNumber/GasLimit are not tracked in BeaconState post-gloas
251
+ // (EIP-7732 removed latestExecutionPayloadHeader). Using 0 as unavailable fallback until
252
+ // a solution is found. The 0 doesn't gate validation in practice: at boot the head's
253
+ // PENDING variant's `executionPayloadBlockHash` is the *parent's* payload hash (per the
254
+ // PENDING/EMPTY convention), so gossip bids that reference the head's *own* payload
255
+ // hash won't match this variant anyway and will IGNORE until `onExecutionPayload`
256
+ // upgrades the head to FULL with real values.
237
257
  executionPayloadNumber: isStatePostGloas(unfinalizedState) ? 0 : unfinalizedState.payloadBlockNumber,
258
+ executionPayloadGasLimit: isStatePostGloas(unfinalizedState)
259
+ ? 0
260
+ : unfinalizedState.latestExecutionPayloadHeader.gasLimit,
238
261
  executionStatus: blockHeader.slot === GENESIS_SLOT ? ExecutionStatus.Valid : ExecutionStatus.Syncing,
239
262
  }
240
263
  : {executionPayloadBlockHash: null, executionStatus: ExecutionStatus.PreMerge}),