@lodestar/beacon-node 1.41.0-dev.21f758891e → 1.41.0-dev.253ed58dd1

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 +102 -9
  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 +154 -11
  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,12 @@
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,
6
7
  ForkPostDeneb,
7
8
  ForkPostFulu,
9
+ ForkPostGloas,
8
10
  ForkPreGloas,
9
11
  ForkSeq,
10
12
  isForkPostAltair,
@@ -16,6 +18,8 @@ import {
16
18
  CachedBeaconStateBellatrix,
17
19
  CachedBeaconStateCapella,
18
20
  CachedBeaconStateExecutions,
21
+ CachedBeaconStateGloas,
22
+ G2_POINT_AT_INFINITY,
19
23
  computeTimeAtSlot,
20
24
  getExpectedWithdrawals,
21
25
  getRandaoMix,
@@ -42,6 +46,7 @@ import {
42
46
  deneb,
43
47
  electra,
44
48
  fulu,
49
+ gloas,
45
50
  } from "@lodestar/types";
46
51
  import {Logger, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
47
52
  import {ZERO_HASH_HEX} from "../../constants/index.js";
@@ -99,6 +104,20 @@ export type AssembledBodyType<T extends BlockType> = T extends BlockType.Full
99
104
  : BlindedBeaconBlockBody;
100
105
  export type AssembledBlockType<T extends BlockType> = T extends BlockType.Full ? BeaconBlock : BlindedBeaconBlock;
101
106
 
107
+ export type ProduceFullGloas = {
108
+ type: BlockType.Full;
109
+ fork: ForkPostGloas;
110
+ executionPayload: ExecutionPayload<ForkPostGloas>;
111
+ executionRequests: electra.ExecutionRequests;
112
+ blobsBundle: BlobsBundle<ForkPostGloas>;
113
+ cells: fulu.Cell[][];
114
+ /**
115
+ * Cached envelope state root computed during block production.
116
+ * This is the state root after running `processExecutionPayloadEnvelope` on the
117
+ * post-block state, and later used to construct the `ExecutionPayloadEnvelope`.
118
+ */
119
+ envelopeStateRoot: Root;
120
+ };
102
121
  export type ProduceFullFulu = {
103
122
  type: BlockType.Full;
104
123
  fork: ForkPostFulu;
@@ -131,6 +150,7 @@ export type ProduceBlinded = {
131
150
 
132
151
  /** The result of local block production, everything that's not the block itself */
133
152
  export type ProduceResult =
153
+ | ProduceFullGloas
134
154
  | ProduceFullFulu
135
155
  | ProduceFullDeneb
136
156
  | ProduceFullBellatrix
@@ -180,12 +200,112 @@ export async function produceBlockBody<T extends BlockType>(
180
200
  this.logger.verbose("Producing beacon block body", logMeta);
181
201
 
182
202
  if (isForkPostGloas(fork)) {
183
- // TODO GLOAS: Set body.signedExecutionPayloadBid and body.payloadAttestation
203
+ // TODO GLOAS: support non self-building here, the block type differentiation between
204
+ // full and blinded no longer makes sense in gloas, it might be a good idea to move
205
+ // this into a completely separate function and have pre/post gloas more separated
206
+ const gloasState = currentState as CachedBeaconStateGloas;
207
+ const safeBlockHash = getSafeExecutionBlockHash(this.forkChoice);
208
+ const finalizedBlockHash = this.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
209
+ const feeRecipient = requestedFeeRecipient ?? this.beaconProposerCache.getOrDefault(proposerIndex);
210
+
211
+ const endExecutionPayload = this.metrics?.executionBlockProductionTimeSteps.startTimer();
212
+
213
+ this.logger.verbose("Preparing execution payload from engine", {
214
+ slot: blockSlot,
215
+ parentBlockRoot: toRootHex(parentBlockRoot),
216
+ feeRecipient,
217
+ });
218
+
219
+ // Get execution payload from EL
220
+ const prepareRes = await prepareExecutionPayload(
221
+ this,
222
+ this.logger,
223
+ fork,
224
+ parentBlockRoot,
225
+ safeBlockHash,
226
+ finalizedBlockHash ?? ZERO_HASH_HEX,
227
+ gloasState,
228
+ feeRecipient
229
+ );
230
+
231
+ const {prepType, payloadId} = prepareRes;
232
+ Object.assign(logMeta, {executionPayloadPrepType: prepType});
233
+
234
+ if (prepType !== PayloadPreparationType.Cached) {
235
+ await sleep(PAYLOAD_GENERATION_TIME_MS);
236
+ }
237
+
238
+ this.logger.verbose("Fetching execution payload from engine", {slot: blockSlot, payloadId});
239
+ const payloadRes = await this.executionEngine.getPayload(fork, payloadId);
240
+
241
+ endExecutionPayload?.({step: BlockProductionStep.executionPayload});
242
+
243
+ const {executionPayload, blobsBundle, executionRequests} = payloadRes;
244
+ executionPayloadValue = payloadRes.executionPayloadValue;
245
+ shouldOverrideBuilder = payloadRes.shouldOverrideBuilder;
246
+
247
+ if (blobsBundle === undefined) {
248
+ throw Error(`Missing blobsBundle response from getPayload at fork=${fork}`);
249
+ }
250
+ if (executionRequests === undefined) {
251
+ throw Error(`Missing executionRequests response from getPayload at fork=${fork}`);
252
+ }
253
+
254
+ const cells = blobsBundle.blobs.map((blob) => kzg.computeCells(blob));
255
+ if (this.opts.sanityCheckExecutionEngineBlobs) {
256
+ await validateCellsAndKzgCommitments(blobsBundle.commitments, blobsBundle.proofs, cells);
257
+ }
258
+
259
+ // Create self-build execution payload bid
260
+ const bid: gloas.ExecutionPayloadBid = {
261
+ parentBlockHash: gloasState.latestBlockHash,
262
+ parentBlockRoot: parentBlockRoot,
263
+ blockHash: executionPayload.blockHash,
264
+ prevRandao: getRandaoMix(gloasState, gloasState.epochCtx.epoch),
265
+ feeRecipient: executionPayload.feeRecipient,
266
+ gasLimit: BigInt(executionPayload.gasLimit),
267
+ builderIndex: BUILDER_INDEX_SELF_BUILD,
268
+ slot: blockSlot,
269
+ value: 0,
270
+ executionPayment: 0,
271
+ blobKzgCommitments: blobsBundle.commitments,
272
+ };
273
+ const signedBid: gloas.SignedExecutionPayloadBid = {
274
+ message: bid,
275
+ signature: G2_POINT_AT_INFINITY,
276
+ };
277
+
184
278
  const commonBlockBody = await commonBlockBodyPromise;
185
- blockBody = Object.assign({}, commonBlockBody) as AssembledBodyType<T>;
186
- executionPayloadValue = BigInt(0);
279
+ const gloasBody = Object.assign({}, commonBlockBody) as gloas.BeaconBlockBody;
280
+ gloasBody.signedExecutionPayloadBid = signedBid;
281
+ // TODO GLOAS: Get payload attestations from pool for previous slot
282
+ gloasBody.payloadAttestations = [];
283
+ blockBody = gloasBody as AssembledBodyType<T>;
284
+
285
+ // Store execution payload data required to construct execution payload envelope later
286
+ const gloasResult = produceResult as ProduceFullGloas;
287
+ gloasResult.executionPayload = executionPayload as ExecutionPayload<ForkPostGloas>;
288
+ gloasResult.executionRequests = executionRequests;
289
+ gloasResult.blobsBundle = blobsBundle;
290
+ gloasResult.cells = cells;
291
+
292
+ const fetchedTime = Date.now() / 1000 - computeTimeAtSlot(this.config, blockSlot, this.genesisTime);
293
+ this.metrics?.blockPayload.payloadFetchedTime.observe({prepType}, fetchedTime);
294
+ this.logger.verbose("Produced block with self-build bid", {
295
+ slot: blockSlot,
296
+ executionPayloadValue,
297
+ prepType,
298
+ payloadId,
299
+ fetchedTime,
300
+ executionBlockHash: toRootHex(executionPayload.blockHash),
301
+ blobs: blobsBundle.commitments.length,
302
+ });
187
303
 
188
- // We don't deal with blinded blocks, execution engine, blobs and execution requests post-gloas
304
+ Object.assign(logMeta, {
305
+ transactions: executionPayload.transactions.length,
306
+ blobs: blobsBundle.commitments.length,
307
+ shouldOverrideBuilder,
308
+ });
189
309
  } else if (isForkPostBellatrix(fork)) {
190
310
  const safeBlockHash = getSafeExecutionBlockHash(this.forkChoice);
191
311
  const finalizedBlockHash = this.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
@@ -480,10 +600,12 @@ export async function prepareExecutionPayload(
480
600
  parentBlockRoot: Root,
481
601
  safeBlockHash: RootHex,
482
602
  finalizedBlockHash: RootHex,
483
- state: CachedBeaconStateExecutions,
603
+ state: CachedBeaconStateExecutions | CachedBeaconStateGloas,
484
604
  suggestedFeeRecipient: string
485
605
  ): Promise<{prepType: PayloadPreparationType; payloadId: PayloadId}> {
486
- const parentHash = state.latestExecutionPayloadHeader.blockHash;
606
+ const parentHash = isForkPostGloas(fork)
607
+ ? (state as CachedBeaconStateGloas).latestBlockHash
608
+ : (state as CachedBeaconStateExecutions).latestExecutionPayloadHeader.blockHash;
487
609
  const timestamp = computeTimeAtSlot(chain.config, state.slot, state.genesisTime);
488
610
  const prevRandao = getRandaoMix(state, state.epochCtx.epoch);
489
611
 
@@ -568,25 +690,46 @@ export function getPayloadAttributesForSSE(
568
690
  fork: ForkPostBellatrix,
569
691
  chain: {
570
692
  config: ChainForkConfig;
693
+ forkChoice: IForkChoice;
571
694
  },
572
695
  {
573
696
  prepareState,
574
697
  prepareSlot,
575
698
  parentBlockRoot,
576
699
  feeRecipient,
577
- }: {prepareState: CachedBeaconStateExecutions; prepareSlot: Slot; parentBlockRoot: Root; feeRecipient: string}
700
+ }: {
701
+ prepareState: CachedBeaconStateExecutions | CachedBeaconStateGloas;
702
+ prepareSlot: Slot;
703
+ parentBlockRoot: Root;
704
+ feeRecipient: string;
705
+ }
578
706
  ): SSEPayloadAttributes {
579
- const parentHash = prepareState.latestExecutionPayloadHeader.blockHash;
707
+ const parentHash = isForkPostGloas(fork)
708
+ ? (prepareState as CachedBeaconStateGloas).latestBlockHash
709
+ : (prepareState as CachedBeaconStateExecutions).latestExecutionPayloadHeader.blockHash;
580
710
  const payloadAttributes = preparePayloadAttributes(fork, chain, {
581
711
  prepareState,
582
712
  prepareSlot,
583
713
  parentBlockRoot,
584
714
  feeRecipient,
585
715
  });
716
+
717
+ let parentBlockNumber: number;
718
+ if (isForkPostGloas(fork)) {
719
+ // TODO GLOAS: revisit this after fork choice changes are merged
720
+ const parentBlock = chain.forkChoice.getBlock(parentBlockRoot);
721
+ if (parentBlock?.executionPayloadBlockHash == null) {
722
+ throw Error(`Parent block not found in fork choice root=${toRootHex(parentBlockRoot)}`);
723
+ }
724
+ parentBlockNumber = parentBlock.executionPayloadNumber;
725
+ } else {
726
+ parentBlockNumber = (prepareState as CachedBeaconStateExecutions).latestExecutionPayloadHeader.blockNumber;
727
+ }
728
+
586
729
  const ssePayloadAttributes: SSEPayloadAttributes = {
587
730
  proposerIndex: prepareState.epochCtx.getBeaconProposer(prepareSlot),
588
731
  proposalSlot: prepareSlot,
589
- parentBlockNumber: prepareState.latestExecutionPayloadHeader.blockNumber,
732
+ parentBlockNumber,
590
733
  parentBlockRoot,
591
734
  parentBlockHash: parentHash,
592
735
  payloadAttributes,
@@ -605,7 +748,7 @@ function preparePayloadAttributes(
605
748
  parentBlockRoot,
606
749
  feeRecipient,
607
750
  }: {
608
- prepareState: CachedBeaconStateExecutions;
751
+ prepareState: CachedBeaconStateExecutions | CachedBeaconStateGloas;
609
752
  prepareSlot: Slot;
610
753
  parentBlockRoot: Root;
611
754
  feeRecipient: string;
@@ -10,7 +10,7 @@ import {
10
10
  getBlockHeaderProposerSignatureSetByHeaderSlot,
11
11
  getBlockHeaderProposerSignatureSetByParentStateSlot,
12
12
  } from "@lodestar/state-transition";
13
- import {Root, Slot, SubnetID, fulu, ssz} from "@lodestar/types";
13
+ import {DataColumnSidecar, Root, Slot, SubnetID, fulu, ssz} from "@lodestar/types";
14
14
  import {byteArrayEquals, toRootHex, verifyMerkleBranch} from "@lodestar/utils";
15
15
  import {Metrics} from "../../metrics/metrics.js";
16
16
  import {kzg} from "../../util/kzg.js";
@@ -457,9 +457,6 @@ export async function validateBlockDataColumnSidecars(
457
457
  * SPEC FUNCTION
458
458
  * https://github.com/ethereum/consensus-specs/blob/v1.6.0-alpha.4/specs/fulu/p2p-interface.md#compute_subnet_for_data_column_sidecar
459
459
  */
460
- export function computeSubnetForDataColumnSidecar(
461
- config: ChainConfig,
462
- columnSidecar: fulu.DataColumnSidecar
463
- ): SubnetID {
460
+ export function computeSubnetForDataColumnSidecar(config: ChainConfig, columnSidecar: DataColumnSidecar): SubnetID {
464
461
  return columnSidecar.index % config.DATA_COLUMN_SIDECAR_SUBNET_COUNT;
465
462
  }