@lodestar/beacon-node 1.41.0-dev.279c11da24 → 1.41.0-dev.49f680d9f0

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 (167) hide show
  1. package/lib/api/impl/beacon/blocks/index.d.ts.map +1 -1
  2. package/lib/api/impl/beacon/blocks/index.js +121 -3
  3. package/lib/api/impl/beacon/blocks/index.js.map +1 -1
  4. package/lib/api/impl/debug/index.d.ts.map +1 -1
  5. package/lib/api/impl/debug/index.js +5 -2
  6. package/lib/api/impl/debug/index.js.map +1 -1
  7. package/lib/api/impl/lightclient/index.d.ts.map +1 -1
  8. package/lib/api/impl/lightclient/index.js +19 -2
  9. package/lib/api/impl/lightclient/index.js.map +1 -1
  10. package/lib/api/impl/validator/index.d.ts.map +1 -1
  11. package/lib/api/impl/validator/index.js +101 -1
  12. package/lib/api/impl/validator/index.js.map +1 -1
  13. package/lib/chain/archiveStore/archiveStore.d.ts +1 -0
  14. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  15. package/lib/chain/archiveStore/archiveStore.js +9 -0
  16. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  17. package/lib/chain/archiveStore/utils/archivePayloads.d.ts +7 -0
  18. package/lib/chain/archiveStore/utils/archivePayloads.d.ts.map +1 -0
  19. package/lib/chain/archiveStore/utils/archivePayloads.js +10 -0
  20. package/lib/chain/archiveStore/utils/archivePayloads.js.map +1 -0
  21. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  22. package/lib/chain/blocks/importBlock.js +0 -2
  23. package/lib/chain/blocks/importBlock.js.map +1 -1
  24. package/lib/chain/blocks/index.d.ts.map +1 -1
  25. package/lib/chain/blocks/index.js +2 -1
  26. package/lib/chain/blocks/index.js.map +1 -1
  27. package/lib/chain/blocks/writeBlockInputToDb.d.ts.map +1 -1
  28. package/lib/chain/blocks/writeBlockInputToDb.js +3 -0
  29. package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
  30. package/lib/chain/chain.d.ts +2 -2
  31. package/lib/chain/chain.d.ts.map +1 -1
  32. package/lib/chain/chain.js +24 -6
  33. package/lib/chain/chain.js.map +1 -1
  34. package/lib/chain/emitter.d.ts +2 -2
  35. package/lib/chain/emitter.d.ts.map +1 -1
  36. package/lib/chain/interface.d.ts +2 -2
  37. package/lib/chain/interface.d.ts.map +1 -1
  38. package/lib/chain/lightClient/index.d.ts.map +1 -1
  39. package/lib/chain/lightClient/index.js +1 -1
  40. package/lib/chain/lightClient/index.js.map +1 -1
  41. package/lib/chain/options.d.ts.map +1 -1
  42. package/lib/chain/options.js.map +1 -1
  43. package/lib/chain/prepareNextSlot.js +3 -3
  44. package/lib/chain/prepareNextSlot.js.map +1 -1
  45. package/lib/chain/produceBlock/computeNewStateRoot.d.ts +10 -2
  46. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  47. package/lib/chain/produceBlock/computeNewStateRoot.js +24 -2
  48. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  49. package/lib/chain/produceBlock/produceBlockBody.d.ts +22 -7
  50. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  51. package/lib/chain/produceBlock/produceBlockBody.js +110 -10
  52. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  53. package/lib/chain/validation/dataColumnSidecar.d.ts +2 -2
  54. package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
  55. package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
  56. package/lib/db/beacon.d.ts +3 -1
  57. package/lib/db/beacon.d.ts.map +1 -1
  58. package/lib/db/beacon.js +5 -1
  59. package/lib/db/beacon.js.map +1 -1
  60. package/lib/db/buckets.d.ts +3 -1
  61. package/lib/db/buckets.d.ts.map +1 -1
  62. package/lib/db/buckets.js +2 -0
  63. package/lib/db/buckets.js.map +1 -1
  64. package/lib/db/interface.d.ts +3 -1
  65. package/lib/db/interface.d.ts.map +1 -1
  66. package/lib/db/repositories/blockArchiveIndex.d.ts +2 -2
  67. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  68. package/lib/db/repositories/dataColumnSidecar.d.ts +5 -3
  69. package/lib/db/repositories/dataColumnSidecar.d.ts.map +1 -1
  70. package/lib/db/repositories/dataColumnSidecar.js +14 -1
  71. package/lib/db/repositories/dataColumnSidecar.js.map +1 -1
  72. package/lib/db/repositories/dataColumnSidecarArchive.d.ts +5 -3
  73. package/lib/db/repositories/dataColumnSidecarArchive.d.ts.map +1 -1
  74. package/lib/db/repositories/dataColumnSidecarArchive.js +14 -1
  75. package/lib/db/repositories/dataColumnSidecarArchive.js.map +1 -1
  76. package/lib/db/repositories/executionPayloadEnvelope.d.ts +19 -0
  77. package/lib/db/repositories/executionPayloadEnvelope.d.ts.map +1 -0
  78. package/lib/db/repositories/executionPayloadEnvelope.js +22 -0
  79. package/lib/db/repositories/executionPayloadEnvelope.js.map +1 -0
  80. package/lib/db/repositories/executionPayloadEnvelopeArchive.d.ts +18 -0
  81. package/lib/db/repositories/executionPayloadEnvelopeArchive.d.ts.map +1 -0
  82. package/lib/db/repositories/executionPayloadEnvelopeArchive.js +28 -0
  83. package/lib/db/repositories/executionPayloadEnvelopeArchive.js.map +1 -0
  84. package/lib/db/repositories/index.d.ts +2 -0
  85. package/lib/db/repositories/index.d.ts.map +1 -1
  86. package/lib/db/repositories/index.js +2 -0
  87. package/lib/db/repositories/index.js.map +1 -1
  88. package/lib/metrics/metrics/beacon.d.ts +1 -0
  89. package/lib/metrics/metrics/beacon.d.ts.map +1 -1
  90. package/lib/metrics/metrics/beacon.js +5 -0
  91. package/lib/metrics/metrics/beacon.js.map +1 -1
  92. package/lib/metrics/metrics/lodestar.d.ts +5 -0
  93. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  94. package/lib/metrics/metrics/lodestar.js +9 -0
  95. package/lib/metrics/metrics/lodestar.js.map +1 -1
  96. package/lib/network/gossip/interface.d.ts +3 -3
  97. package/lib/network/gossip/interface.d.ts.map +1 -1
  98. package/lib/network/gossip/topic.d.ts +113 -63
  99. package/lib/network/gossip/topic.d.ts.map +1 -1
  100. package/lib/network/gossip/topic.js +2 -2
  101. package/lib/network/gossip/topic.js.map +1 -1
  102. package/lib/network/interface.d.ts +3 -2
  103. package/lib/network/interface.d.ts.map +1 -1
  104. package/lib/network/network.d.ts +3 -2
  105. package/lib/network/network.d.ts.map +1 -1
  106. package/lib/network/network.js +10 -1
  107. package/lib/network/network.js.map +1 -1
  108. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  109. package/lib/network/processor/gossipHandlers.js +5 -1
  110. package/lib/network/processor/gossipHandlers.js.map +1 -1
  111. package/lib/network/reqresp/handlers/lightClientUpdatesByRange.d.ts.map +1 -1
  112. package/lib/network/reqresp/handlers/lightClientUpdatesByRange.js +7 -1
  113. package/lib/network/reqresp/handlers/lightClientUpdatesByRange.js.map +1 -1
  114. package/lib/util/blobs.d.ts +2 -2
  115. package/lib/util/blobs.d.ts.map +1 -1
  116. package/lib/util/blobs.js.map +1 -1
  117. package/lib/util/dataColumns.d.ts +11 -3
  118. package/lib/util/dataColumns.d.ts.map +1 -1
  119. package/lib/util/dataColumns.js +27 -0
  120. package/lib/util/dataColumns.js.map +1 -1
  121. package/lib/util/multifork.d.ts +8 -0
  122. package/lib/util/multifork.d.ts.map +1 -1
  123. package/lib/util/multifork.js +37 -0
  124. package/lib/util/multifork.js.map +1 -1
  125. package/lib/util/serializedCache.d.ts +5 -0
  126. package/lib/util/serializedCache.d.ts.map +1 -1
  127. package/lib/util/serializedCache.js +5 -0
  128. package/lib/util/serializedCache.js.map +1 -1
  129. package/package.json +15 -15
  130. package/src/api/impl/beacon/blocks/index.ts +145 -2
  131. package/src/api/impl/debug/index.ts +8 -5
  132. package/src/api/impl/lightclient/index.ts +19 -2
  133. package/src/api/impl/validator/index.ts +124 -1
  134. package/src/chain/archiveStore/archiveStore.ts +10 -0
  135. package/src/chain/archiveStore/utils/archivePayloads.ts +15 -0
  136. package/src/chain/blocks/importBlock.ts +0 -3
  137. package/src/chain/blocks/index.ts +2 -1
  138. package/src/chain/blocks/writeBlockInputToDb.ts +3 -0
  139. package/src/chain/chain.ts +41 -11
  140. package/src/chain/emitter.ts +2 -2
  141. package/src/chain/interface.ts +2 -2
  142. package/src/chain/lightClient/index.ts +4 -1
  143. package/src/chain/options.ts +1 -0
  144. package/src/chain/prepareNextSlot.ts +5 -5
  145. package/src/chain/produceBlock/computeNewStateRoot.ts +35 -3
  146. package/src/chain/produceBlock/produceBlockBody.ts +163 -13
  147. package/src/chain/validation/dataColumnSidecar.ts +2 -5
  148. package/src/db/beacon.ts +8 -0
  149. package/src/db/buckets.ts +3 -0
  150. package/src/db/interface.ts +5 -0
  151. package/src/db/repositories/dataColumnSidecar.ts +18 -3
  152. package/src/db/repositories/dataColumnSidecarArchive.ts +18 -3
  153. package/src/db/repositories/executionPayloadEnvelope.ts +26 -0
  154. package/src/db/repositories/executionPayloadEnvelopeArchive.ts +32 -0
  155. package/src/db/repositories/index.ts +2 -0
  156. package/src/metrics/metrics/beacon.ts +5 -0
  157. package/src/metrics/metrics/lodestar.ts +9 -0
  158. package/src/network/gossip/interface.ts +3 -3
  159. package/src/network/gossip/topic.ts +2 -1
  160. package/src/network/interface.ts +4 -1
  161. package/src/network/network.ts +21 -3
  162. package/src/network/processor/gossipHandlers.ts +7 -1
  163. package/src/network/reqresp/handlers/lightClientUpdatesByRange.ts +6 -1
  164. package/src/util/blobs.ts +3 -3
  165. package/src/util/dataColumns.ts +37 -1
  166. package/src/util/multifork.ts +45 -0
  167. package/src/util/serializedCache.ts +5 -0
@@ -1,5 +1,6 @@
1
1
  import {CheckpointWithHex} from "@lodestar/fork-choice";
2
2
  import {LoggerNode} from "@lodestar/logger/node";
3
+ import {ForkSeq} from "@lodestar/params";
3
4
  import {Checkpoint} from "@lodestar/types/phase0";
4
5
  import {callFnWhenAwait} from "@lodestar/utils";
5
6
  import {IBeaconDb} from "../../db/index.js";
@@ -13,6 +14,7 @@ import {HistoricalStateRegen} from "./historicalState/historicalStateRegen.js";
13
14
  import {ArchiveMode, ArchiveStoreOpts, StateArchiveStrategy} from "./interface.js";
14
15
  import {FrequencyStateArchiveStrategy} from "./strategies/frequencyStateArchiveStrategy.js";
15
16
  import {archiveBlocks} from "./utils/archiveBlocks.js";
17
+ import {archiveExecutionPayloadEnvelopes} from "./utils/archivePayloads.js";
16
18
  import {pruneHistory} from "./utils/pruneHistory.js";
17
19
  import {updateBackfillRange} from "./utils/updateBackfillRange.js";
18
20
 
@@ -27,6 +29,7 @@ type ArchiveStoreInitOpts = ArchiveStoreOpts & {dbName: string; anchorState: {fi
27
29
 
28
30
  export enum ArchiveStoreTask {
29
31
  ArchiveBlocks = "archive_blocks",
32
+ ArchivePayloads = "archive_payloads",
30
33
  PruneHistory = "prune_history",
31
34
  OnFinalizedCheckpoint = "on_finalized_checkpoint",
32
35
  MaybeArchiveState = "maybe_archive_state",
@@ -189,6 +192,7 @@ export class ArchiveStore {
189
192
  private processFinalizedCheckpoint = async (finalized: CheckpointWithHex): Promise<void> => {
190
193
  try {
191
194
  const finalizedEpoch = finalized.epoch;
195
+ const finalizedFork = this.chain.config.getForkSeqAtEpoch(finalizedEpoch);
192
196
  this.logger.verbose("Start processing finalized checkpoint", {epoch: finalizedEpoch, rootHex: finalized.rootHex});
193
197
 
194
198
  let timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer();
@@ -206,6 +210,12 @@ export class ArchiveStore {
206
210
  );
207
211
  timer?.({source: ArchiveStoreTask.ArchiveBlocks});
208
212
 
213
+ if (finalizedFork >= ForkSeq.gloas) {
214
+ timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer();
215
+ await archiveExecutionPayloadEnvelopes(this.chain, finalized);
216
+ timer?.({source: ArchiveStoreTask.ArchivePayloads});
217
+ }
218
+
209
219
  if (this.opts.pruneHistory) {
210
220
  timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer();
211
221
  await pruneHistory(
@@ -0,0 +1,15 @@
1
+ import {CheckpointWithHex} from "@lodestar/fork-choice";
2
+ import {IBeaconChain} from "../../interface.js";
3
+
4
+ /**
5
+ * Archives execution payload envelopes from hot DB to archive DB after finalization.
6
+ */
7
+ export async function archiveExecutionPayloadEnvelopes(
8
+ chain: IBeaconChain,
9
+ _finalized: CheckpointWithHex
10
+ ): Promise<void> {
11
+ const finalizedBlock = chain.forkChoice.getFinalizedBlock();
12
+ if (!finalizedBlock) return;
13
+
14
+ // TODO GLOAS: Implement payload envelope archival after epbs fork choice changes are merged
15
+ }
@@ -101,9 +101,6 @@ export async function importBlock(
101
101
  }
102
102
  });
103
103
 
104
- // Without forcefully clearing this cache, we would rely on WeakMap to evict memory which is not reliable
105
- this.serializedCache.clear();
106
-
107
104
  // 2. Import block to fork choice
108
105
 
109
106
  // Should compute checkpoint balances before forkchoice.onBlock
@@ -1,6 +1,7 @@
1
1
  import {SignedBeaconBlock} from "@lodestar/types";
2
2
  import {isErrorAborted, toRootHex} from "@lodestar/utils";
3
3
  import {Metrics} from "../../metrics/metrics.js";
4
+ import {nextEventLoop} from "../../util/eventLoop.js";
4
5
  import {JobItemQueue, isQueueErrorAborted} from "../../util/queue/index.js";
5
6
  import type {BeaconChain} from "../chain.js";
6
7
  import {BlockError, BlockErrorCode, isBlockErrorAborted} from "../errors/index.js";
@@ -100,9 +101,9 @@ export async function processBlocks(
100
101
  );
101
102
 
102
103
  for (const fullyVerifiedBlock of fullyVerifiedBlocks) {
103
- // No need to sleep(0) here since `importBlock` includes a disk write
104
104
  // TODO: Consider batching importBlock too if it takes significant time
105
105
  await importBlock.call(this, fullyVerifiedBlock, opts);
106
+ await nextEventLoop();
106
107
  }
107
108
  } catch (e) {
108
109
  if (isErrorAborted(e) || isQueueErrorAborted(e) || isBlockErrorAborted(e)) {
@@ -125,6 +125,9 @@ export async function persistBlockInputs(this: BeaconChain, blockInputs: IBlockI
125
125
  for (const blockInput of blockInputs) {
126
126
  this.seenBlockInputCache.prune(blockInput.blockRootHex);
127
127
  }
128
+ // Without forcefully clearing this cache, we would rely on WeakMap to evict memory which is not reliable.
129
+ // Clear here (after the DB write) so that writeBlockInputToDb can still use the cached serialized bytes.
130
+ this.serializedCache.clear();
128
131
  if (blockInputs.length === 1) {
129
132
  this.logger.debug("Pruned block input", {
130
133
  slot: blockInputs[0].slot,
@@ -5,11 +5,20 @@ import {CompositeTypeAny, TreeView, Type} from "@chainsafe/ssz";
5
5
  import {BeaconConfig} from "@lodestar/config";
6
6
  import {CheckpointWithHex, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice";
7
7
  import {LoggerNode} from "@lodestar/logger/node";
8
- import {EFFECTIVE_BALANCE_INCREMENT, GENESIS_SLOT, SLOTS_PER_EPOCH, isForkPostElectra} from "@lodestar/params";
8
+ import {
9
+ BUILDER_INDEX_SELF_BUILD,
10
+ EFFECTIVE_BALANCE_INCREMENT,
11
+ ForkPostFulu,
12
+ GENESIS_SLOT,
13
+ SLOTS_PER_EPOCH,
14
+ isForkPostElectra,
15
+ isForkPostGloas,
16
+ } from "@lodestar/params";
9
17
  import {
10
18
  BeaconStateAllForks,
11
19
  BeaconStateElectra,
12
20
  CachedBeaconStateAllForks,
21
+ CachedBeaconStateGloas,
13
22
  EffectiveBalanceIncrements,
14
23
  EpochShuffling,
15
24
  Index2PubkeyCache,
@@ -28,6 +37,7 @@ import {
28
37
  BeaconBlock,
29
38
  BlindedBeaconBlock,
30
39
  BlindedBeaconBlockBody,
40
+ DataColumnSidecars,
31
41
  Epoch,
32
42
  Root,
33
43
  RootHex,
@@ -38,7 +48,7 @@ import {
38
48
  ValidatorIndex,
39
49
  Wei,
40
50
  deneb,
41
- fulu,
51
+ gloas,
42
52
  isBlindedBeaconBlock,
43
53
  phase0,
44
54
  rewards,
@@ -87,8 +97,8 @@ import {
87
97
  } from "./opPools/index.js";
88
98
  import {IChainOptions} from "./options.js";
89
99
  import {PrepareNextSlotScheduler} from "./prepareNextSlot.js";
90
- import {computeNewStateRoot} from "./produceBlock/computeNewStateRoot.js";
91
- import {AssembledBlockType, BlockType, ProduceResult} from "./produceBlock/index.js";
100
+ import {computeEnvelopeStateRoot, computeNewStateRoot} from "./produceBlock/computeNewStateRoot.js";
101
+ import {AssembledBlockType, BlockType, ProduceFullGloas, ProduceResult} from "./produceBlock/index.js";
92
102
  import {BlockAttributes, produceBlockBody, produceCommonBlockBody} from "./produceBlock/produceBlockBody.js";
93
103
  import {QueuedStateRegenerator, RegenCaller} from "./regen/index.js";
94
104
  import {ReprocessController} from "./reprocess.js";
@@ -808,7 +818,7 @@ export class BeaconChain implements IBeaconChain {
808
818
  return null;
809
819
  }
810
820
 
811
- async getDataColumnSidecars(blockSlot: Slot, blockRootHex: string): Promise<fulu.DataColumnSidecars> {
821
+ async getDataColumnSidecars(blockSlot: Slot, blockRootHex: string): Promise<DataColumnSidecars> {
812
822
  const blockInput = this.seenBlockInputCache.get(blockRootHex);
813
823
  if (blockInput) {
814
824
  if (!isBlockInputColumns(blockInput)) {
@@ -818,10 +828,10 @@ export class BeaconChain implements IBeaconChain {
818
828
  }
819
829
  const sidecarsUnfinalized = await this.db.dataColumnSidecar.values(fromHex(blockRootHex));
820
830
  if (sidecarsUnfinalized.length > 0) {
821
- return sidecarsUnfinalized;
831
+ return sidecarsUnfinalized as DataColumnSidecars;
822
832
  }
823
833
  const sidecarsFinalized = await this.db.dataColumnSidecarArchive.values(blockSlot);
824
- return sidecarsFinalized;
834
+ return sidecarsFinalized as DataColumnSidecars;
825
835
  }
826
836
 
827
837
  async getSerializedDataColumnSidecars(
@@ -843,7 +853,7 @@ export class BeaconChain implements IBeaconChain {
843
853
  if (serialized) {
844
854
  return serialized;
845
855
  }
846
- return ssz.fulu.DataColumnSidecar.serialize(sidecar);
856
+ return sszTypesFor(blockInput.forkName as ForkPostFulu).DataColumnSidecar.serialize(sidecar);
847
857
  });
848
858
  }
849
859
  const sidecarsUnfinalized = await this.db.dataColumnSidecar.getManyBinary(fromHex(blockRootHex), indices);
@@ -902,6 +912,7 @@ export class BeaconChain implements IBeaconChain {
902
912
  consensusBlockValue: Wei;
903
913
  shouldOverrideBuilder?: boolean;
904
914
  }> {
915
+ const fork = this.config.getForkName(slot);
905
916
  const state = await this.regen.getBlockSlotState(
906
917
  parentBlock,
907
918
  slot,
@@ -930,7 +941,7 @@ export class BeaconChain implements IBeaconChain {
930
941
  // The hashtree root computed here for debug log will get cached and hence won't introduce additional delays
931
942
  const bodyRoot =
932
943
  produceResult.type === BlockType.Full
933
- ? this.config.getForkTypes(slot).BeaconBlockBody.hashTreeRoot(body)
944
+ ? sszTypesFor(fork).BeaconBlockBody.hashTreeRoot(body)
934
945
  : this.config
935
946
  .getPostBellatrixForkTypes(slot)
936
947
  .BlindedBeaconBlockBody.hashTreeRoot(body as BlindedBeaconBlockBody);
@@ -948,14 +959,33 @@ export class BeaconChain implements IBeaconChain {
948
959
  body,
949
960
  } as AssembledBlockType<T>;
950
961
 
951
- const {newStateRoot, proposerReward} = computeNewStateRoot(this.metrics, state, block);
962
+ const {newStateRoot, proposerReward, postState} = computeNewStateRoot(this.metrics, state, block);
952
963
  block.stateRoot = newStateRoot;
953
964
  const blockRoot =
954
965
  produceResult.type === BlockType.Full
955
- ? this.config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block)
966
+ ? sszTypesFor(fork).BeaconBlock.hashTreeRoot(block)
956
967
  : this.config.getPostBellatrixForkTypes(slot).BlindedBeaconBlock.hashTreeRoot(block as BlindedBeaconBlock);
957
968
  const blockRootHex = toRootHex(blockRoot);
958
969
 
970
+ if (isForkPostGloas(fork)) {
971
+ // TODO GLOAS: we should retire BlockType post-gloas, may need a new enum for self vs non-self built
972
+ if (produceResult.type !== BlockType.Full) {
973
+ throw Error(`Unexpected block type=${produceResult.type} for post-gloas fork=${fork}`);
974
+ }
975
+
976
+ const gloasResult = produceResult as ProduceFullGloas;
977
+ const envelope: gloas.ExecutionPayloadEnvelope = {
978
+ payload: gloasResult.executionPayload,
979
+ executionRequests: gloasResult.executionRequests,
980
+ builderIndex: BUILDER_INDEX_SELF_BUILD,
981
+ beaconBlockRoot: blockRoot,
982
+ slot,
983
+ stateRoot: ZERO_HASH,
984
+ };
985
+ const envelopeStateRoot = computeEnvelopeStateRoot(this.metrics, postState as CachedBeaconStateGloas, envelope);
986
+ gloasResult.envelopeStateRoot = envelopeStateRoot;
987
+ }
988
+
959
989
  // Track the produced block for consensus broadcast validations, later validation, etc.
960
990
  this.blockProductionCache.set(blockRootHex, produceResult);
961
991
  this.metrics?.blockProductionCacheSize.set(this.blockProductionCache.size);
@@ -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 {CachedBeaconStateAllForks} from "@lodestar/state-transition";
6
- import {RootHex, deneb, fulu, phase0} from "@lodestar/types";
6
+ import {DataColumnSidecars, RootHex, 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
 
@@ -88,7 +88,7 @@ export type IChainEvents = ApiEvents & {
88
88
 
89
89
  [ChainEvent.updateTargetCustodyGroupCount]: (targetGroupCount: number) => void;
90
90
 
91
- [ChainEvent.publishDataColumns]: (sidecars: fulu.DataColumnSidecar[]) => void;
91
+ [ChainEvent.publishDataColumns]: (sidecars: DataColumnSidecars) => void;
92
92
 
93
93
  [ChainEvent.publishBlobSidecars]: (sidecars: deneb.BlobSidecar[]) => void;
94
94
 
@@ -11,6 +11,7 @@ import {
11
11
  import {
12
12
  BeaconBlock,
13
13
  BlindedBeaconBlock,
14
+ DataColumnSidecars,
14
15
  Epoch,
15
16
  Root,
16
17
  RootHex,
@@ -23,7 +24,6 @@ import {
23
24
  altair,
24
25
  capella,
25
26
  deneb,
26
- fulu,
27
27
  phase0,
28
28
  rewards,
29
29
  } from "@lodestar/types";
@@ -224,7 +224,7 @@ export interface IBeaconChain {
224
224
  ): Promise<{block: SignedBeaconBlock; executionOptimistic: boolean; finalized: boolean} | null>;
225
225
  getBlobSidecars(blockSlot: Slot, blockRootHex: string): Promise<deneb.BlobSidecars | null>;
226
226
  getSerializedBlobSidecars(blockSlot: Slot, blockRootHex: string): Promise<Uint8Array | null>;
227
- getDataColumnSidecars(blockSlot: Slot, blockRootHex: string): Promise<fulu.DataColumnSidecars>;
227
+ getDataColumnSidecars(blockSlot: Slot, blockRootHex: string): Promise<DataColumnSidecars>;
228
228
  getSerializedDataColumnSidecars(
229
229
  blockSlot: Slot,
230
230
  blockRootHex: string,
@@ -355,7 +355,10 @@ export class LightClientServer {
355
355
  // Signature data
356
356
  const update = await this.db.bestLightClientUpdate.get(period);
357
357
  if (!update) {
358
- throw Error(`No partialUpdate available for period ${period}`);
358
+ throw new LightClientServerError(
359
+ {code: LightClientServerErrorCode.RESOURCE_UNAVAILABLE},
360
+ `No partialUpdate available for period ${period}`
361
+ );
359
362
  }
360
363
  return update;
361
364
  }
@@ -27,6 +27,7 @@ export type IChainOptions = BlockProcessOpts &
27
27
  blsVerifyAllMainThread?: boolean;
28
28
  blsVerifyAllMultiThread?: boolean;
29
29
  blacklistedBlocks?: string[];
30
+ // TODO GLOAS: add similar option for execution payload envelopes?
30
31
  persistProducedBlocks?: boolean;
31
32
  persistInvalidSszObjects?: boolean;
32
33
  persistInvalidSszObjectsDir?: string;
@@ -1,14 +1,14 @@
1
1
  import {routes} from "@lodestar/api";
2
2
  import {ChainForkConfig} from "@lodestar/config";
3
3
  import {getSafeExecutionBlockHash} from "@lodestar/fork-choice";
4
- import {ForkPostBellatrix, ForkSeq, SLOTS_PER_EPOCH} from "@lodestar/params";
4
+ import {ForkPostBellatrix, ForkSeq, SLOTS_PER_EPOCH, isForkPostBellatrix} from "@lodestar/params";
5
5
  import {
6
6
  CachedBeaconStateAllForks,
7
7
  CachedBeaconStateExecutions,
8
+ CachedBeaconStateGloas,
8
9
  StateHashTreeRootSource,
9
10
  computeEpochAtSlot,
10
11
  computeTimeAtSlot,
11
- isExecutionStateType,
12
12
  } from "@lodestar/state-transition";
13
13
  import {Slot} from "@lodestar/types";
14
14
  import {Logger, fromHex, isErrorAborted, sleep} from "@lodestar/utils";
@@ -120,10 +120,10 @@ export class PrepareNextSlotScheduler {
120
120
  RegenCaller.precomputeEpoch
121
121
  );
122
122
 
123
- if (isExecutionStateType(prepareState)) {
123
+ if (isForkPostBellatrix(fork)) {
124
124
  const proposerIndex = prepareState.epochCtx.getBeaconProposer(prepareSlot);
125
125
  const feeRecipient = this.chain.beaconProposerCache.get(proposerIndex);
126
- let updatedPrepareState = prepareState;
126
+ let updatedPrepareState = prepareState as CachedBeaconStateExecutions | CachedBeaconStateGloas;
127
127
  let updatedHeadRoot = headRoot;
128
128
 
129
129
  if (feeRecipient) {
@@ -146,7 +146,7 @@ export class PrepareNextSlotScheduler {
146
146
  // only transfer cache if epoch transition because that's the state we will use to stateTransition() the 1st block of epoch
147
147
  {dontTransferCache: !isEpochTransition},
148
148
  RegenCaller.predictProposerHead
149
- )) as CachedBeaconStateExecutions;
149
+ )) as CachedBeaconStateExecutions | CachedBeaconStateGloas;
150
150
  updatedHeadRoot = proposerHeadRoot;
151
151
  }
152
152
 
@@ -1,11 +1,14 @@
1
1
  import {
2
2
  CachedBeaconStateAllForks,
3
+ CachedBeaconStateGloas,
3
4
  DataAvailabilityStatus,
4
5
  ExecutionPayloadStatus,
6
+ G2_POINT_AT_INFINITY,
5
7
  StateHashTreeRootSource,
6
8
  stateTransition,
7
9
  } from "@lodestar/state-transition";
8
- import {BeaconBlock, BlindedBeaconBlock, Gwei, Root} from "@lodestar/types";
10
+ import {processExecutionPayloadEnvelope} from "@lodestar/state-transition/block";
11
+ import {BeaconBlock, BlindedBeaconBlock, Gwei, Root, gloas} from "@lodestar/types";
9
12
  import {ZERO_HASH} from "../../constants/index.js";
10
13
  import {Metrics} from "../../metrics/index.js";
11
14
 
@@ -18,7 +21,7 @@ export function computeNewStateRoot(
18
21
  metrics: Metrics | null,
19
22
  state: CachedBeaconStateAllForks,
20
23
  block: BeaconBlock | BlindedBeaconBlock
21
- ): {newStateRoot: Root; proposerReward: Gwei} {
24
+ ): {newStateRoot: Root; proposerReward: Gwei; postState: CachedBeaconStateAllForks} {
22
25
  // Set signature to zero to re-use stateTransition() function which requires the SignedBeaconBlock type
23
26
  const blockEmptySig = {message: block, signature: ZERO_HASH};
24
27
 
@@ -51,5 +54,34 @@ export function computeNewStateRoot(
51
54
  const newStateRoot = postState.hashTreeRoot();
52
55
  hashTreeRootTimer?.();
53
56
 
54
- return {newStateRoot, proposerReward};
57
+ return {newStateRoot, proposerReward, postState};
58
+ }
59
+
60
+ /**
61
+ * Compute the state root after processing an execution payload envelope.
62
+ * Similar to `computeNewStateRoot` but for payload envelope processing.
63
+ *
64
+ * The `postBlockState` is mutated in place, callers must ensure it is not needed afterward.
65
+ */
66
+ export function computeEnvelopeStateRoot(
67
+ metrics: Metrics | null,
68
+ postBlockState: CachedBeaconStateGloas,
69
+ envelope: gloas.ExecutionPayloadEnvelope
70
+ ): Root {
71
+ const signedEnvelope: gloas.SignedExecutionPayloadEnvelope = {
72
+ message: envelope,
73
+ signature: G2_POINT_AT_INFINITY,
74
+ };
75
+
76
+ const processEnvelopeTimer = metrics?.blockPayload.executionPayloadEnvelopeProcessingTime.startTimer();
77
+ processExecutionPayloadEnvelope(postBlockState, signedEnvelope, false);
78
+ processEnvelopeTimer?.();
79
+
80
+ const hashTreeRootTimer = metrics?.stateHashTreeRootTime.startTimer({
81
+ source: StateHashTreeRootSource.computeEnvelopeStateRoot,
82
+ });
83
+ const stateRoot = postBlockState.hashTreeRoot();
84
+ hashTreeRootTimer?.();
85
+
86
+ return stateRoot;
55
87
  }
@@ -1,10 +1,13 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
- import {ProtoBlock, getSafeExecutionBlockHash} from "@lodestar/fork-choice";
2
+ import {IForkChoice, ProtoBlock, getSafeExecutionBlockHash} from "@lodestar/fork-choice";
3
3
  import {
4
+ BUILDER_INDEX_SELF_BUILD,
4
5
  ForkName,
5
6
  ForkPostBellatrix,
7
+ ForkPostCapella,
6
8
  ForkPostDeneb,
7
9
  ForkPostFulu,
10
+ ForkPostGloas,
8
11
  ForkPreGloas,
9
12
  ForkSeq,
10
13
  isForkPostAltair,
@@ -16,6 +19,8 @@ import {
16
19
  CachedBeaconStateBellatrix,
17
20
  CachedBeaconStateCapella,
18
21
  CachedBeaconStateExecutions,
22
+ CachedBeaconStateGloas,
23
+ G2_POINT_AT_INFINITY,
19
24
  computeTimeAtSlot,
20
25
  getExpectedWithdrawals,
21
26
  getRandaoMix,
@@ -42,6 +47,7 @@ import {
42
47
  deneb,
43
48
  electra,
44
49
  fulu,
50
+ gloas,
45
51
  } from "@lodestar/types";
46
52
  import {Logger, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
47
53
  import {ZERO_HASH_HEX} from "../../constants/index.js";
@@ -99,6 +105,20 @@ export type AssembledBodyType<T extends BlockType> = T extends BlockType.Full
99
105
  : BlindedBeaconBlockBody;
100
106
  export type AssembledBlockType<T extends BlockType> = T extends BlockType.Full ? BeaconBlock : BlindedBeaconBlock;
101
107
 
108
+ export type ProduceFullGloas = {
109
+ type: BlockType.Full;
110
+ fork: ForkPostGloas;
111
+ executionPayload: ExecutionPayload<ForkPostGloas>;
112
+ executionRequests: electra.ExecutionRequests;
113
+ blobsBundle: BlobsBundle<ForkPostGloas>;
114
+ cells: fulu.Cell[][];
115
+ /**
116
+ * Cached envelope state root computed during block production.
117
+ * This is the state root after running `processExecutionPayloadEnvelope` on the
118
+ * post-block state, and later used to construct the `ExecutionPayloadEnvelope`.
119
+ */
120
+ envelopeStateRoot: Root;
121
+ };
102
122
  export type ProduceFullFulu = {
103
123
  type: BlockType.Full;
104
124
  fork: ForkPostFulu;
@@ -131,6 +151,7 @@ export type ProduceBlinded = {
131
151
 
132
152
  /** The result of local block production, everything that's not the block itself */
133
153
  export type ProduceResult =
154
+ | ProduceFullGloas
134
155
  | ProduceFullFulu
135
156
  | ProduceFullDeneb
136
157
  | ProduceFullBellatrix
@@ -180,12 +201,112 @@ export async function produceBlockBody<T extends BlockType>(
180
201
  this.logger.verbose("Producing beacon block body", logMeta);
181
202
 
182
203
  if (isForkPostGloas(fork)) {
183
- // TODO GLOAS: Set body.signedExecutionPayloadBid and body.payloadAttestation
204
+ // TODO GLOAS: support non self-building here, the block type differentiation between
205
+ // full and blinded no longer makes sense in gloas, it might be a good idea to move
206
+ // this into a completely separate function and have pre/post gloas more separated
207
+ const gloasState = currentState as CachedBeaconStateGloas;
208
+ const safeBlockHash = getSafeExecutionBlockHash(this.forkChoice);
209
+ const finalizedBlockHash = this.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
210
+ const feeRecipient = requestedFeeRecipient ?? this.beaconProposerCache.getOrDefault(proposerIndex);
211
+
212
+ const endExecutionPayload = this.metrics?.executionBlockProductionTimeSteps.startTimer();
213
+
214
+ this.logger.verbose("Preparing execution payload from engine", {
215
+ slot: blockSlot,
216
+ parentBlockRoot: toRootHex(parentBlockRoot),
217
+ feeRecipient,
218
+ });
219
+
220
+ // Get execution payload from EL
221
+ const prepareRes = await prepareExecutionPayload(
222
+ this,
223
+ this.logger,
224
+ fork,
225
+ parentBlockRoot,
226
+ safeBlockHash,
227
+ finalizedBlockHash ?? ZERO_HASH_HEX,
228
+ gloasState,
229
+ feeRecipient
230
+ );
231
+
232
+ const {prepType, payloadId} = prepareRes;
233
+ Object.assign(logMeta, {executionPayloadPrepType: prepType});
234
+
235
+ if (prepType !== PayloadPreparationType.Cached) {
236
+ await sleep(PAYLOAD_GENERATION_TIME_MS);
237
+ }
238
+
239
+ this.logger.verbose("Fetching execution payload from engine", {slot: blockSlot, payloadId});
240
+ const payloadRes = await this.executionEngine.getPayload(fork, payloadId);
241
+
242
+ endExecutionPayload?.({step: BlockProductionStep.executionPayload});
243
+
244
+ const {executionPayload, blobsBundle, executionRequests} = payloadRes;
245
+ executionPayloadValue = payloadRes.executionPayloadValue;
246
+ shouldOverrideBuilder = payloadRes.shouldOverrideBuilder;
247
+
248
+ if (blobsBundle === undefined) {
249
+ throw Error(`Missing blobsBundle response from getPayload at fork=${fork}`);
250
+ }
251
+ if (executionRequests === undefined) {
252
+ throw Error(`Missing executionRequests response from getPayload at fork=${fork}`);
253
+ }
254
+
255
+ const cells = blobsBundle.blobs.map((blob) => kzg.computeCells(blob));
256
+ if (this.opts.sanityCheckExecutionEngineBlobs) {
257
+ await validateCellsAndKzgCommitments(blobsBundle.commitments, blobsBundle.proofs, cells);
258
+ }
259
+
260
+ // Create self-build execution payload bid
261
+ const bid: gloas.ExecutionPayloadBid = {
262
+ parentBlockHash: gloasState.latestBlockHash,
263
+ parentBlockRoot: parentBlockRoot,
264
+ blockHash: executionPayload.blockHash,
265
+ prevRandao: getRandaoMix(gloasState, gloasState.epochCtx.epoch),
266
+ feeRecipient: executionPayload.feeRecipient,
267
+ gasLimit: BigInt(executionPayload.gasLimit),
268
+ builderIndex: BUILDER_INDEX_SELF_BUILD,
269
+ slot: blockSlot,
270
+ value: 0,
271
+ executionPayment: 0,
272
+ blobKzgCommitments: blobsBundle.commitments,
273
+ };
274
+ const signedBid: gloas.SignedExecutionPayloadBid = {
275
+ message: bid,
276
+ signature: G2_POINT_AT_INFINITY,
277
+ };
278
+
184
279
  const commonBlockBody = await commonBlockBodyPromise;
185
- blockBody = Object.assign({}, commonBlockBody) as AssembledBodyType<T>;
186
- executionPayloadValue = BigInt(0);
280
+ const gloasBody = Object.assign({}, commonBlockBody) as gloas.BeaconBlockBody;
281
+ gloasBody.signedExecutionPayloadBid = signedBid;
282
+ // TODO GLOAS: Get payload attestations from pool for previous slot
283
+ gloasBody.payloadAttestations = [];
284
+ blockBody = gloasBody as AssembledBodyType<T>;
285
+
286
+ // Store execution payload data required to construct execution payload envelope later
287
+ const gloasResult = produceResult as ProduceFullGloas;
288
+ gloasResult.executionPayload = executionPayload as ExecutionPayload<ForkPostGloas>;
289
+ gloasResult.executionRequests = executionRequests;
290
+ gloasResult.blobsBundle = blobsBundle;
291
+ gloasResult.cells = cells;
292
+
293
+ const fetchedTime = Date.now() / 1000 - computeTimeAtSlot(this.config, blockSlot, this.genesisTime);
294
+ this.metrics?.blockPayload.payloadFetchedTime.observe({prepType}, fetchedTime);
295
+ this.logger.verbose("Produced block with self-build bid", {
296
+ slot: blockSlot,
297
+ executionPayloadValue,
298
+ prepType,
299
+ payloadId,
300
+ fetchedTime,
301
+ executionBlockHash: toRootHex(executionPayload.blockHash),
302
+ blobs: blobsBundle.commitments.length,
303
+ });
187
304
 
188
- // We don't deal with blinded blocks, execution engine, blobs and execution requests post-gloas
305
+ Object.assign(logMeta, {
306
+ transactions: executionPayload.transactions.length,
307
+ blobs: blobsBundle.commitments.length,
308
+ shouldOverrideBuilder,
309
+ });
189
310
  } else if (isForkPostBellatrix(fork)) {
190
311
  const safeBlockHash = getSafeExecutionBlockHash(this.forkChoice);
191
312
  const finalizedBlockHash = this.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
@@ -447,8 +568,14 @@ export async function produceBlockBody<T extends BlockType>(
447
568
  });
448
569
  }
449
570
 
450
- if (ForkSeq[fork] >= ForkSeq.capella) {
451
- const {blsToExecutionChanges, executionPayload} = blockBody as capella.BeaconBlockBody;
571
+ if (ForkSeq[fork] >= ForkSeq.gloas) {
572
+ const {blsToExecutionChanges, payloadAttestations} = blockBody as BeaconBlockBody<ForkPostGloas>;
573
+ Object.assign(logMeta, {
574
+ blsToExecutionChanges: blsToExecutionChanges.length,
575
+ payloadAttestations: payloadAttestations.length,
576
+ });
577
+ } else if (ForkSeq[fork] >= ForkSeq.capella) {
578
+ const {blsToExecutionChanges, executionPayload} = blockBody as BeaconBlockBody<ForkPostCapella & ForkPreGloas>;
452
579
  Object.assign(logMeta, {
453
580
  blsToExecutionChanges: blsToExecutionChanges.length,
454
581
  });
@@ -480,10 +607,12 @@ export async function prepareExecutionPayload(
480
607
  parentBlockRoot: Root,
481
608
  safeBlockHash: RootHex,
482
609
  finalizedBlockHash: RootHex,
483
- state: CachedBeaconStateExecutions,
610
+ state: CachedBeaconStateExecutions | CachedBeaconStateGloas,
484
611
  suggestedFeeRecipient: string
485
612
  ): Promise<{prepType: PayloadPreparationType; payloadId: PayloadId}> {
486
- const parentHash = state.latestExecutionPayloadHeader.blockHash;
613
+ const parentHash = isForkPostGloas(fork)
614
+ ? (state as CachedBeaconStateGloas).latestBlockHash
615
+ : (state as CachedBeaconStateExecutions).latestExecutionPayloadHeader.blockHash;
487
616
  const timestamp = computeTimeAtSlot(chain.config, state.slot, state.genesisTime);
488
617
  const prevRandao = getRandaoMix(state, state.epochCtx.epoch);
489
618
 
@@ -568,25 +697,46 @@ export function getPayloadAttributesForSSE(
568
697
  fork: ForkPostBellatrix,
569
698
  chain: {
570
699
  config: ChainForkConfig;
700
+ forkChoice: IForkChoice;
571
701
  },
572
702
  {
573
703
  prepareState,
574
704
  prepareSlot,
575
705
  parentBlockRoot,
576
706
  feeRecipient,
577
- }: {prepareState: CachedBeaconStateExecutions; prepareSlot: Slot; parentBlockRoot: Root; feeRecipient: string}
707
+ }: {
708
+ prepareState: CachedBeaconStateExecutions | CachedBeaconStateGloas;
709
+ prepareSlot: Slot;
710
+ parentBlockRoot: Root;
711
+ feeRecipient: string;
712
+ }
578
713
  ): SSEPayloadAttributes {
579
- const parentHash = prepareState.latestExecutionPayloadHeader.blockHash;
714
+ const parentHash = isForkPostGloas(fork)
715
+ ? (prepareState as CachedBeaconStateGloas).latestBlockHash
716
+ : (prepareState as CachedBeaconStateExecutions).latestExecutionPayloadHeader.blockHash;
580
717
  const payloadAttributes = preparePayloadAttributes(fork, chain, {
581
718
  prepareState,
582
719
  prepareSlot,
583
720
  parentBlockRoot,
584
721
  feeRecipient,
585
722
  });
723
+
724
+ let parentBlockNumber: number;
725
+ if (isForkPostGloas(fork)) {
726
+ // TODO GLOAS: revisit this after fork choice changes are merged
727
+ const parentBlock = chain.forkChoice.getBlock(parentBlockRoot);
728
+ if (parentBlock?.executionPayloadBlockHash == null) {
729
+ throw Error(`Parent block not found in fork choice root=${toRootHex(parentBlockRoot)}`);
730
+ }
731
+ parentBlockNumber = parentBlock.executionPayloadNumber;
732
+ } else {
733
+ parentBlockNumber = (prepareState as CachedBeaconStateExecutions).latestExecutionPayloadHeader.blockNumber;
734
+ }
735
+
586
736
  const ssePayloadAttributes: SSEPayloadAttributes = {
587
737
  proposerIndex: prepareState.epochCtx.getBeaconProposer(prepareSlot),
588
738
  proposalSlot: prepareSlot,
589
- parentBlockNumber: prepareState.latestExecutionPayloadHeader.blockNumber,
739
+ parentBlockNumber,
590
740
  parentBlockRoot,
591
741
  parentBlockHash: parentHash,
592
742
  payloadAttributes,
@@ -605,7 +755,7 @@ function preparePayloadAttributes(
605
755
  parentBlockRoot,
606
756
  feeRecipient,
607
757
  }: {
608
- prepareState: CachedBeaconStateExecutions;
758
+ prepareState: CachedBeaconStateExecutions | CachedBeaconStateGloas;
609
759
  prepareSlot: Slot;
610
760
  parentBlockRoot: Root;
611
761
  feeRecipient: string;