@lodestar/beacon-node 1.41.0-dev.9fa839a030 → 1.41.0-dev.aeb5a213ee

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 (175) 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/beacon/state/utils.d.ts.map +1 -1
  5. package/lib/api/impl/beacon/state/utils.js +1 -20
  6. package/lib/api/impl/beacon/state/utils.js.map +1 -1
  7. package/lib/api/impl/debug/index.d.ts.map +1 -1
  8. package/lib/api/impl/debug/index.js +5 -2
  9. package/lib/api/impl/debug/index.js.map +1 -1
  10. package/lib/api/impl/lightclient/index.d.ts.map +1 -1
  11. package/lib/api/impl/lightclient/index.js +19 -2
  12. package/lib/api/impl/lightclient/index.js.map +1 -1
  13. package/lib/api/impl/validator/index.d.ts.map +1 -1
  14. package/lib/api/impl/validator/index.js +101 -1
  15. package/lib/api/impl/validator/index.js.map +1 -1
  16. package/lib/chain/archiveStore/archiveStore.d.ts +1 -0
  17. package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
  18. package/lib/chain/archiveStore/archiveStore.js +9 -0
  19. package/lib/chain/archiveStore/archiveStore.js.map +1 -1
  20. package/lib/chain/archiveStore/utils/archivePayloads.d.ts +7 -0
  21. package/lib/chain/archiveStore/utils/archivePayloads.d.ts.map +1 -0
  22. package/lib/chain/archiveStore/utils/archivePayloads.js +10 -0
  23. package/lib/chain/archiveStore/utils/archivePayloads.js.map +1 -0
  24. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  25. package/lib/chain/blocks/importBlock.js +0 -2
  26. package/lib/chain/blocks/importBlock.js.map +1 -1
  27. package/lib/chain/blocks/index.d.ts.map +1 -1
  28. package/lib/chain/blocks/index.js +2 -1
  29. package/lib/chain/blocks/index.js.map +1 -1
  30. package/lib/chain/blocks/writeBlockInputToDb.d.ts.map +1 -1
  31. package/lib/chain/blocks/writeBlockInputToDb.js +3 -0
  32. package/lib/chain/blocks/writeBlockInputToDb.js.map +1 -1
  33. package/lib/chain/chain.d.ts +2 -2
  34. package/lib/chain/chain.d.ts.map +1 -1
  35. package/lib/chain/chain.js +24 -6
  36. package/lib/chain/chain.js.map +1 -1
  37. package/lib/chain/emitter.d.ts +2 -2
  38. package/lib/chain/emitter.d.ts.map +1 -1
  39. package/lib/chain/interface.d.ts +2 -2
  40. package/lib/chain/interface.d.ts.map +1 -1
  41. package/lib/chain/lightClient/index.d.ts.map +1 -1
  42. package/lib/chain/lightClient/index.js +1 -1
  43. package/lib/chain/lightClient/index.js.map +1 -1
  44. package/lib/chain/options.d.ts.map +1 -1
  45. package/lib/chain/options.js.map +1 -1
  46. package/lib/chain/prepareNextSlot.js +3 -3
  47. package/lib/chain/prepareNextSlot.js.map +1 -1
  48. package/lib/chain/produceBlock/computeNewStateRoot.d.ts +10 -2
  49. package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
  50. package/lib/chain/produceBlock/computeNewStateRoot.js +24 -2
  51. package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
  52. package/lib/chain/produceBlock/produceBlockBody.d.ts +22 -7
  53. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  54. package/lib/chain/produceBlock/produceBlockBody.js +110 -10
  55. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  56. package/lib/chain/validation/dataColumnSidecar.d.ts +2 -2
  57. package/lib/chain/validation/dataColumnSidecar.d.ts.map +1 -1
  58. package/lib/chain/validation/dataColumnSidecar.js.map +1 -1
  59. package/lib/db/beacon.d.ts +3 -1
  60. package/lib/db/beacon.d.ts.map +1 -1
  61. package/lib/db/beacon.js +5 -1
  62. package/lib/db/beacon.js.map +1 -1
  63. package/lib/db/buckets.d.ts +3 -1
  64. package/lib/db/buckets.d.ts.map +1 -1
  65. package/lib/db/buckets.js +2 -0
  66. package/lib/db/buckets.js.map +1 -1
  67. package/lib/db/interface.d.ts +3 -1
  68. package/lib/db/interface.d.ts.map +1 -1
  69. package/lib/db/repositories/blockArchiveIndex.d.ts +2 -2
  70. package/lib/db/repositories/blockArchiveIndex.d.ts.map +1 -1
  71. package/lib/db/repositories/dataColumnSidecar.d.ts +5 -3
  72. package/lib/db/repositories/dataColumnSidecar.d.ts.map +1 -1
  73. package/lib/db/repositories/dataColumnSidecar.js +14 -1
  74. package/lib/db/repositories/dataColumnSidecar.js.map +1 -1
  75. package/lib/db/repositories/dataColumnSidecarArchive.d.ts +5 -3
  76. package/lib/db/repositories/dataColumnSidecarArchive.d.ts.map +1 -1
  77. package/lib/db/repositories/dataColumnSidecarArchive.js +14 -1
  78. package/lib/db/repositories/dataColumnSidecarArchive.js.map +1 -1
  79. package/lib/db/repositories/executionPayloadEnvelope.d.ts +19 -0
  80. package/lib/db/repositories/executionPayloadEnvelope.d.ts.map +1 -0
  81. package/lib/db/repositories/executionPayloadEnvelope.js +22 -0
  82. package/lib/db/repositories/executionPayloadEnvelope.js.map +1 -0
  83. package/lib/db/repositories/executionPayloadEnvelopeArchive.d.ts +18 -0
  84. package/lib/db/repositories/executionPayloadEnvelopeArchive.d.ts.map +1 -0
  85. package/lib/db/repositories/executionPayloadEnvelopeArchive.js +28 -0
  86. package/lib/db/repositories/executionPayloadEnvelopeArchive.js.map +1 -0
  87. package/lib/db/repositories/index.d.ts +2 -0
  88. package/lib/db/repositories/index.d.ts.map +1 -1
  89. package/lib/db/repositories/index.js +2 -0
  90. package/lib/db/repositories/index.js.map +1 -1
  91. package/lib/metrics/metrics/beacon.d.ts +1 -0
  92. package/lib/metrics/metrics/beacon.d.ts.map +1 -1
  93. package/lib/metrics/metrics/beacon.js +5 -0
  94. package/lib/metrics/metrics/beacon.js.map +1 -1
  95. package/lib/metrics/metrics/lodestar.d.ts +5 -0
  96. package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
  97. package/lib/metrics/metrics/lodestar.js +9 -0
  98. package/lib/metrics/metrics/lodestar.js.map +1 -1
  99. package/lib/metrics/metrics.d.ts.map +1 -1
  100. package/lib/metrics/metrics.js +8 -3
  101. package/lib/metrics/metrics.js.map +1 -1
  102. package/lib/network/gossip/interface.d.ts +3 -3
  103. package/lib/network/gossip/interface.d.ts.map +1 -1
  104. package/lib/network/gossip/topic.d.ts +113 -63
  105. package/lib/network/gossip/topic.d.ts.map +1 -1
  106. package/lib/network/gossip/topic.js +2 -2
  107. package/lib/network/gossip/topic.js.map +1 -1
  108. package/lib/network/interface.d.ts +3 -2
  109. package/lib/network/interface.d.ts.map +1 -1
  110. package/lib/network/network.d.ts +3 -2
  111. package/lib/network/network.d.ts.map +1 -1
  112. package/lib/network/network.js +10 -1
  113. package/lib/network/network.js.map +1 -1
  114. package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
  115. package/lib/network/processor/gossipHandlers.js +5 -1
  116. package/lib/network/processor/gossipHandlers.js.map +1 -1
  117. package/lib/network/reqresp/handlers/lightClientUpdatesByRange.d.ts.map +1 -1
  118. package/lib/network/reqresp/handlers/lightClientUpdatesByRange.js +7 -1
  119. package/lib/network/reqresp/handlers/lightClientUpdatesByRange.js.map +1 -1
  120. package/lib/util/blobs.d.ts +2 -2
  121. package/lib/util/blobs.d.ts.map +1 -1
  122. package/lib/util/blobs.js.map +1 -1
  123. package/lib/util/dataColumns.d.ts +11 -3
  124. package/lib/util/dataColumns.d.ts.map +1 -1
  125. package/lib/util/dataColumns.js +27 -0
  126. package/lib/util/dataColumns.js.map +1 -1
  127. package/lib/util/multifork.d.ts +8 -0
  128. package/lib/util/multifork.d.ts.map +1 -1
  129. package/lib/util/multifork.js +37 -0
  130. package/lib/util/multifork.js.map +1 -1
  131. package/lib/util/serializedCache.d.ts +5 -0
  132. package/lib/util/serializedCache.d.ts.map +1 -1
  133. package/lib/util/serializedCache.js +5 -0
  134. package/lib/util/serializedCache.js.map +1 -1
  135. package/package.json +15 -15
  136. package/src/api/impl/beacon/blocks/index.ts +145 -2
  137. package/src/api/impl/beacon/state/utils.ts +10 -23
  138. package/src/api/impl/debug/index.ts +8 -5
  139. package/src/api/impl/lightclient/index.ts +19 -2
  140. package/src/api/impl/validator/index.ts +124 -1
  141. package/src/chain/archiveStore/archiveStore.ts +10 -0
  142. package/src/chain/archiveStore/utils/archivePayloads.ts +15 -0
  143. package/src/chain/blocks/importBlock.ts +0 -3
  144. package/src/chain/blocks/index.ts +2 -1
  145. package/src/chain/blocks/writeBlockInputToDb.ts +3 -0
  146. package/src/chain/chain.ts +41 -11
  147. package/src/chain/emitter.ts +2 -2
  148. package/src/chain/interface.ts +2 -2
  149. package/src/chain/lightClient/index.ts +4 -1
  150. package/src/chain/options.ts +1 -0
  151. package/src/chain/prepareNextSlot.ts +5 -5
  152. package/src/chain/produceBlock/computeNewStateRoot.ts +35 -3
  153. package/src/chain/produceBlock/produceBlockBody.ts +163 -13
  154. package/src/chain/validation/dataColumnSidecar.ts +2 -5
  155. package/src/db/beacon.ts +8 -0
  156. package/src/db/buckets.ts +3 -0
  157. package/src/db/interface.ts +5 -0
  158. package/src/db/repositories/dataColumnSidecar.ts +18 -3
  159. package/src/db/repositories/dataColumnSidecarArchive.ts +18 -3
  160. package/src/db/repositories/executionPayloadEnvelope.ts +26 -0
  161. package/src/db/repositories/executionPayloadEnvelopeArchive.ts +32 -0
  162. package/src/db/repositories/index.ts +2 -0
  163. package/src/metrics/metrics/beacon.ts +5 -0
  164. package/src/metrics/metrics/lodestar.ts +9 -0
  165. package/src/metrics/metrics.ts +8 -3
  166. package/src/network/gossip/interface.ts +3 -3
  167. package/src/network/gossip/topic.ts +2 -1
  168. package/src/network/interface.ts +4 -1
  169. package/src/network/network.ts +21 -3
  170. package/src/network/processor/gossipHandlers.ts +7 -1
  171. package/src/network/reqresp/handlers/lightClientUpdatesByRange.ts +6 -1
  172. package/src/util/blobs.ts +3 -3
  173. package/src/util/dataColumns.ts +37 -1
  174. package/src/util/multifork.ts +45 -0
  175. package/src/util/serializedCache.ts +5 -0
@@ -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;
@@ -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
  }
package/src/db/beacon.ts CHANGED
@@ -15,6 +15,8 @@ import {
15
15
  CheckpointHeaderRepository,
16
16
  DataColumnSidecarArchiveRepository,
17
17
  DataColumnSidecarRepository,
18
+ ExecutionPayloadEnvelopeArchiveRepository,
19
+ ExecutionPayloadEnvelopeRepository,
18
20
  ProposerSlashingRepository,
19
21
  StateArchiveRepository,
20
22
  SyncCommitteeRepository,
@@ -36,6 +38,9 @@ export class BeaconDb implements IBeaconDb {
36
38
  dataColumnSidecar: DataColumnSidecarRepository;
37
39
  dataColumnSidecarArchive: DataColumnSidecarArchiveRepository;
38
40
 
41
+ executionPayloadEnvelope: ExecutionPayloadEnvelopeRepository;
42
+ executionPayloadEnvelopeArchive: ExecutionPayloadEnvelopeArchiveRepository;
43
+
39
44
  stateArchive: StateArchiveRepository;
40
45
  checkpointState: CheckpointStateRepository;
41
46
 
@@ -65,6 +70,9 @@ export class BeaconDb implements IBeaconDb {
65
70
  this.dataColumnSidecar = new DataColumnSidecarRepository(config, db);
66
71
  this.dataColumnSidecarArchive = new DataColumnSidecarArchiveRepository(config, db);
67
72
 
73
+ this.executionPayloadEnvelope = new ExecutionPayloadEnvelopeRepository(config, db);
74
+ this.executionPayloadEnvelopeArchive = new ExecutionPayloadEnvelopeArchiveRepository(config, db);
75
+
68
76
  this.stateArchive = new StateArchiveRepository(config, db);
69
77
  this.checkpointState = new CheckpointStateRepository(config, db);
70
78
  this.voluntaryExit = new VoluntaryExitRepository(config, db);
package/src/db/buckets.ts CHANGED
@@ -69,6 +69,9 @@ export enum Bucket {
69
69
 
70
70
  fulu_dataColumnSidecars = 57, // FULU BeaconBlockRoot -> DataColumnSidecars
71
71
  fulu_dataColumnSidecarsArchive = 58, // FULU BeaconBlockSlot -> DataColumnSidecars
72
+
73
+ gloas_executionPayloadEnvelope = 59, // GLOAS BeaconBlockRoot -> SignedExecutionPayloadEnvelope
74
+ gloas_executionPayloadEnvelopeArchive = 60, // GLOAS Slot -> SignedExecutionPayloadEnvelope
72
75
  }
73
76
 
74
77
  export function getBucketNameByValue<T extends Bucket>(enumValue: T): keyof typeof Bucket {
@@ -12,6 +12,8 @@ import {
12
12
  CheckpointHeaderRepository,
13
13
  DataColumnSidecarArchiveRepository,
14
14
  DataColumnSidecarRepository,
15
+ ExecutionPayloadEnvelopeArchiveRepository,
16
+ ExecutionPayloadEnvelopeRepository,
15
17
  ProposerSlashingRepository,
16
18
  StateArchiveRepository,
17
19
  SyncCommitteeRepository,
@@ -35,6 +37,9 @@ export interface IBeaconDb {
35
37
  dataColumnSidecar: DataColumnSidecarRepository;
36
38
  dataColumnSidecarArchive: DataColumnSidecarArchiveRepository;
37
39
 
40
+ executionPayloadEnvelope: ExecutionPayloadEnvelopeRepository;
41
+ executionPayloadEnvelopeArchive: ExecutionPayloadEnvelopeArchiveRepository;
42
+
38
43
  // finalized states
39
44
  stateArchive: StateArchiveRepository;
40
45
  // checkpoint states
@@ -1,7 +1,8 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
2
  import {Db, PrefixedRepository, decodeNumberForDbKey, encodeNumberForDbKey} from "@lodestar/db";
3
3
  import {NUMBER_OF_COLUMNS} from "@lodestar/params";
4
- import {ColumnIndex, Root, fulu, ssz} from "@lodestar/types";
4
+ import {ColumnIndex, DataColumnSidecar, Root, isGloasDataColumnSidecar, ssz} from "@lodestar/types";
5
+ import {isGloasDataColumnSidecarBytes} from "../../util/multifork.js";
5
6
  import {Bucket, getBucketNameByValue} from "../buckets.js";
6
7
 
7
8
  const COLUMN_INDEX_BYTE_SIZE = 2;
@@ -15,7 +16,7 @@ type BlockRoot = Root;
15
16
  *
16
17
  * Indexed data by `blockRoot` + `columnIndex`
17
18
  */
18
- export class DataColumnSidecarRepository extends PrefixedRepository<BlockRoot, ColumnIndex, fulu.DataColumnSidecar> {
19
+ export class DataColumnSidecarRepository extends PrefixedRepository<BlockRoot, ColumnIndex, DataColumnSidecar> {
19
20
  constructor(config: ChainForkConfig, db: Db) {
20
21
  const bucket = Bucket.fulu_dataColumnSidecars;
21
22
  super(config, db, bucket, ssz.fulu.DataColumnSidecar, getBucketNameByValue(bucket));
@@ -24,10 +25,24 @@ export class DataColumnSidecarRepository extends PrefixedRepository<BlockRoot, C
24
25
  /**
25
26
  * Id is hashTreeRoot of unsigned BeaconBlock
26
27
  */
27
- getId(value: fulu.DataColumnSidecar): ColumnIndex {
28
+ getId(value: DataColumnSidecar): ColumnIndex {
28
29
  return value.index;
29
30
  }
30
31
 
32
+ encodeValue(value: DataColumnSidecar): Uint8Array {
33
+ if (isGloasDataColumnSidecar(value)) {
34
+ return ssz.gloas.DataColumnSidecar.serialize(value);
35
+ }
36
+ return ssz.fulu.DataColumnSidecar.serialize(value);
37
+ }
38
+
39
+ decodeValue(data: Uint8Array): DataColumnSidecar {
40
+ if (isGloasDataColumnSidecarBytes(data)) {
41
+ return ssz.gloas.DataColumnSidecar.deserialize(data);
42
+ }
43
+ return ssz.fulu.DataColumnSidecar.deserialize(data);
44
+ }
45
+
31
46
  encodeKeyRaw(prefix: BlockRoot, id: ColumnIndex): Uint8Array {
32
47
  return Buffer.concat([prefix, encodeNumberForDbKey(id, COLUMN_INDEX_BYTE_SIZE)]);
33
48
  }
@@ -1,7 +1,8 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
2
  import {Db, PrefixedRepository, decodeNumberForDbKey, encodeNumberForDbKey} from "@lodestar/db";
3
3
  import {NUMBER_OF_COLUMNS} from "@lodestar/params";
4
- import {ColumnIndex, Slot, fulu, ssz} from "@lodestar/types";
4
+ import {ColumnIndex, DataColumnSidecar, Slot, isGloasDataColumnSidecar, ssz} from "@lodestar/types";
5
+ import {isGloasDataColumnSidecarBytes} from "../../util/multifork.js";
5
6
  import {Bucket, getBucketNameByValue} from "../buckets.js";
6
7
 
7
8
  const COLUMN_INDEX_BYTE_SIZE = 2;
@@ -13,7 +14,7 @@ const SLOT_BYTE_SIZE = 8;
13
14
  *
14
15
  * Indexed data by `slot` + `columnIndex`
15
16
  */
16
- export class DataColumnSidecarArchiveRepository extends PrefixedRepository<Slot, ColumnIndex, fulu.DataColumnSidecar> {
17
+ export class DataColumnSidecarArchiveRepository extends PrefixedRepository<Slot, ColumnIndex, DataColumnSidecar> {
17
18
  constructor(config: ChainForkConfig, db: Db) {
18
19
  const bucket = Bucket.fulu_dataColumnSidecarsArchive;
19
20
  super(config, db, bucket, ssz.fulu.DataColumnSidecar, getBucketNameByValue(bucket));
@@ -22,10 +23,24 @@ export class DataColumnSidecarArchiveRepository extends PrefixedRepository<Slot,
22
23
  /**
23
24
  * Id is hashTreeRoot of unsigned BeaconBlock
24
25
  */
25
- getId(value: fulu.DataColumnSidecar): ColumnIndex {
26
+ getId(value: DataColumnSidecar): ColumnIndex {
26
27
  return value.index;
27
28
  }
28
29
 
30
+ encodeValue(value: DataColumnSidecar): Uint8Array {
31
+ if (isGloasDataColumnSidecar(value)) {
32
+ return ssz.gloas.DataColumnSidecar.serialize(value);
33
+ }
34
+ return ssz.fulu.DataColumnSidecar.serialize(value);
35
+ }
36
+
37
+ decodeValue(data: Uint8Array): DataColumnSidecar {
38
+ if (isGloasDataColumnSidecarBytes(data)) {
39
+ return ssz.gloas.DataColumnSidecar.deserialize(data);
40
+ }
41
+ return ssz.fulu.DataColumnSidecar.deserialize(data);
42
+ }
43
+
29
44
  encodeKeyRaw(prefix: Slot, id: ColumnIndex): Uint8Array {
30
45
  return Buffer.concat([
31
46
  encodeNumberForDbKey(prefix, SLOT_BYTE_SIZE),
@@ -0,0 +1,26 @@
1
+ import {ChainForkConfig} from "@lodestar/config";
2
+ import {Db, Repository} from "@lodestar/db";
3
+ import {Root, gloas, ssz} from "@lodestar/types";
4
+ import {Bucket, getBucketNameByValue} from "../buckets.js";
5
+
6
+ type BlockRoot = Root;
7
+
8
+ /**
9
+ * Used to store unfinalized `SignedExecutionPayloadEnvelope`
10
+ *
11
+ * Indexed by beacon block root (root of the beacon block that contains the bid)
12
+ */
13
+ export class ExecutionPayloadEnvelopeRepository extends Repository<BlockRoot, gloas.SignedExecutionPayloadEnvelope> {
14
+ constructor(config: ChainForkConfig, db: Db) {
15
+ const bucket = Bucket.gloas_executionPayloadEnvelope;
16
+ super(config, db, bucket, ssz.gloas.SignedExecutionPayloadEnvelope, getBucketNameByValue(bucket));
17
+ }
18
+
19
+ /**
20
+ * Id is the beacon block root (not execution payload hash)
21
+ * This allows correlation with the block that contains the bid
22
+ */
23
+ getId(value: gloas.SignedExecutionPayloadEnvelope): BlockRoot {
24
+ return value.message.beaconBlockRoot;
25
+ }
26
+ }
@@ -0,0 +1,32 @@
1
+ import {ChainForkConfig} from "@lodestar/config";
2
+ import {BUCKET_LENGTH, Db, Repository, encodeKey as encodeDbKey} from "@lodestar/db";
3
+ import {Slot, gloas, ssz} from "@lodestar/types";
4
+ import {bytesToInt} from "@lodestar/utils";
5
+ import {Bucket, getBucketNameByValue} from "../buckets.js";
6
+
7
+ /**
8
+ * Used to store finalized `SignedExecutionPayloadEnvelope`
9
+ *
10
+ * Indexed by slot for chronological archival
11
+ */
12
+ export class ExecutionPayloadEnvelopeArchiveRepository extends Repository<Slot, gloas.SignedExecutionPayloadEnvelope> {
13
+ constructor(config: ChainForkConfig, db: Db) {
14
+ const bucket = Bucket.gloas_executionPayloadEnvelopeArchive;
15
+ super(config, db, bucket, ssz.gloas.SignedExecutionPayloadEnvelope, getBucketNameByValue(bucket));
16
+ }
17
+
18
+ /**
19
+ * Id is the slot from the envelope
20
+ */
21
+ getId(value: gloas.SignedExecutionPayloadEnvelope): Slot {
22
+ return value.message.slot;
23
+ }
24
+
25
+ encodeKey(id: Slot): Uint8Array {
26
+ return encodeDbKey(this.bucket, id);
27
+ }
28
+
29
+ decodeKey(data: Uint8Array): number {
30
+ return bytesToInt(data.subarray(BUCKET_LENGTH), "be");
31
+ }
32
+ }
@@ -8,6 +8,8 @@ export {BlockArchiveRepository} from "./blockArchive.js";
8
8
  export {BLSToExecutionChangeRepository} from "./blsToExecutionChange.js";
9
9
  export {DataColumnSidecarRepository} from "./dataColumnSidecar.js";
10
10
  export {DataColumnSidecarArchiveRepository} from "./dataColumnSidecarArchive.js";
11
+ export {ExecutionPayloadEnvelopeRepository} from "./executionPayloadEnvelope.js";
12
+ export {ExecutionPayloadEnvelopeArchiveRepository} from "./executionPayloadEnvelopeArchive.js";
11
13
  export {BestLightClientUpdateRepository} from "./lightclientBestUpdate.js";
12
14
  export {CheckpointHeaderRepository} from "./lightclientCheckpointHeader.js";
13
15
  export {SyncCommitteeRepository} from "./lightclientSyncCommittee.js";
@@ -144,6 +144,11 @@ export function createBeaconMetrics(register: RegistryMetricCreator) {
144
144
  help: "Time for preparing payload in advance",
145
145
  buckets: [0.1, 1, 3, 5, 10],
146
146
  }),
147
+ executionPayloadEnvelopeProcessingTime: register.histogram({
148
+ name: "beacon_block_payload_envelope_processing_seconds",
149
+ help: "Time to process execution payload envelope during block production",
150
+ buckets: [0.005, 0.01, 0.05, 0.1, 0.2, 0.5, 1],
151
+ }),
147
152
  payloadFetchedTime: register.histogram<{prepType: PayloadPreparationType}>({
148
153
  name: "beacon_block_payload_fetched_time",
149
154
  help: "Time to fetch the payload from EL",
@@ -827,6 +827,15 @@ export function createLodestarMetrics(
827
827
  help: "Total number of blobs retrieved from execution engine and published to gossip",
828
828
  }),
829
829
  },
830
+ // Gossip execution payload envelope
831
+ gossipExecutionPayloadEnvelope: {
832
+ elapsedTimeTillReceived: register.histogram<{source: OpSource}>({
833
+ name: "lodestar_gossip_execution_payload_envelope_elapsed_time_till_received",
834
+ help: "Time elapsed between slot time and the time execution payload envelope received",
835
+ labelNames: ["source"],
836
+ buckets: [0.5, 1, 2, 4, 6, 12],
837
+ }),
838
+ },
830
839
  recoverDataColumnSidecars: {
831
840
  recoverTime: register.histogram({
832
841
  name: "lodestar_recover_data_column_sidecar_recover_time_seconds",
@@ -19,11 +19,16 @@ export function createMetrics(opts: MetricsOptions, genesisTime: number, externa
19
19
  const lodestar = createLodestarMetrics(register, opts.metadata, genesisTime);
20
20
  const stateTransition = getMetrics(register);
21
21
 
22
- process.on("unhandledRejection", (_error) => {
22
+ const onUnhandledRejection = (_error: unknown): void => {
23
23
  lodestar.unhandledPromiseRejections.inc();
24
- });
24
+ };
25
+ process.on("unhandledRejection", onUnhandledRejection);
25
26
 
26
- const close = collectNodeJSMetrics(register);
27
+ const nodeJsMetricsClose = collectNodeJSMetrics(register);
28
+ const close = (): void => {
29
+ process.removeListener("unhandledRejection", onUnhandledRejection);
30
+ nodeJsMetricsClose();
31
+ };
27
32
 
28
33
  // Merge external registries
29
34
  for (const externalRegister of externalRegistries) {
@@ -4,6 +4,7 @@ import {PeerIdStr} from "@chainsafe/libp2p-gossipsub/types";
4
4
  import {BeaconConfig, ForkBoundary} from "@lodestar/config";
5
5
  import {
6
6
  AttesterSlashing,
7
+ DataColumnSidecar,
7
8
  LightClientFinalityUpdate,
8
9
  LightClientOptimisticUpdate,
9
10
  SignedAggregateAndProof,
@@ -14,7 +15,6 @@ import {
14
15
  altair,
15
16
  capella,
16
17
  deneb,
17
- fulu,
18
18
  gloas,
19
19
  phase0,
20
20
  } from "@lodestar/types";
@@ -98,7 +98,7 @@ export type GossipTypeMap = {
98
98
  [GossipType.blob_sidecar]: deneb.BlobSidecar;
99
99
  [GossipType.beacon_aggregate_and_proof]: SignedAggregateAndProof;
100
100
  [GossipType.beacon_attestation]: SingleAttestation;
101
- [GossipType.data_column_sidecar]: fulu.DataColumnSidecar;
101
+ [GossipType.data_column_sidecar]: DataColumnSidecar;
102
102
  [GossipType.voluntary_exit]: phase0.SignedVoluntaryExit;
103
103
  [GossipType.proposer_slashing]: phase0.ProposerSlashing;
104
104
  [GossipType.attester_slashing]: AttesterSlashing;
@@ -117,7 +117,7 @@ export type GossipFnByType = {
117
117
  [GossipType.blob_sidecar]: (blobSidecar: deneb.BlobSidecar) => Promise<void> | void;
118
118
  [GossipType.beacon_aggregate_and_proof]: (aggregateAndProof: SignedAggregateAndProof) => Promise<void> | void;
119
119
  [GossipType.beacon_attestation]: (attestation: SingleAttestation) => Promise<void> | void;
120
- [GossipType.data_column_sidecar]: (dataColumnSidecar: fulu.DataColumnSidecar) => Promise<void> | void;
120
+ [GossipType.data_column_sidecar]: (dataColumnSidecar: DataColumnSidecar) => Promise<void> | void;
121
121
  [GossipType.voluntary_exit]: (voluntaryExit: phase0.SignedVoluntaryExit) => Promise<void> | void;
122
122
  [GossipType.proposer_slashing]: (proposerSlashing: phase0.ProposerSlashing) => Promise<void> | void;
123
123
  [GossipType.attester_slashing]: (attesterSlashing: AttesterSlashing) => Promise<void> | void;
@@ -6,6 +6,7 @@ import {
6
6
  SYNC_COMMITTEE_SUBNET_COUNT,
7
7
  isForkPostAltair,
8
8
  isForkPostElectra,
9
+ isForkPostFulu,
9
10
  } from "@lodestar/params";
10
11
  import {Attestation, SingleAttestation, ssz, sszTypesFor} from "@lodestar/types";
11
12
  import {GossipAction, GossipActionError, GossipErrorCode} from "../../chain/errors/gossipValidation.js";
@@ -92,7 +93,7 @@ export function getGossipSSZType(topic: GossipTopic) {
92
93
  case GossipType.blob_sidecar:
93
94
  return ssz.deneb.BlobSidecar;
94
95
  case GossipType.data_column_sidecar:
95
- return ssz.fulu.DataColumnSidecar;
96
+ return isForkPostFulu(fork) ? sszTypesFor(fork).DataColumnSidecar : ssz.fulu.DataColumnSidecar;
96
97
  case GossipType.beacon_aggregate_and_proof:
97
98
  return sszTypesFor(fork).SignedAggregateAndProof;
98
99
  case GossipType.beacon_attestation:
@@ -19,6 +19,7 @@ import type {Datastore} from "interface-datastore";
19
19
  import {Libp2p as ILibp2p} from "libp2p";
20
20
  import {
21
21
  AttesterSlashing,
22
+ DataColumnSidecar,
22
23
  LightClientFinalityUpdate,
23
24
  LightClientOptimisticUpdate,
24
25
  SignedAggregateAndProof,
@@ -31,6 +32,7 @@ import {
31
32
  capella,
32
33
  deneb,
33
34
  fulu,
35
+ gloas,
34
36
  phase0,
35
37
  } from "@lodestar/types";
36
38
  import {BlockInputSource} from "../chain/blocks/blockInput/types.js";
@@ -86,7 +88,7 @@ export interface INetwork extends INetworkCorePublic {
86
88
  publishBlobSidecar(blobSidecar: deneb.BlobSidecar): Promise<number>;
87
89
  publishBeaconAggregateAndProof(aggregateAndProof: SignedAggregateAndProof): Promise<number>;
88
90
  publishBeaconAttestation(attestation: SingleAttestation, subnet: SubnetID): Promise<number>;
89
- publishDataColumnSidecar(dataColumnSideCar: fulu.DataColumnSidecar): Promise<number>;
91
+ publishDataColumnSidecar(dataColumnSideCar: DataColumnSidecar): Promise<number>;
90
92
  publishVoluntaryExit(voluntaryExit: phase0.SignedVoluntaryExit): Promise<number>;
91
93
  publishBlsToExecutionChange(blsToExecutionChange: capella.SignedBLSToExecutionChange): Promise<number>;
92
94
  publishProposerSlashing(proposerSlashing: phase0.ProposerSlashing): Promise<number>;
@@ -95,6 +97,7 @@ export interface INetwork extends INetworkCorePublic {
95
97
  publishContributionAndProof(contributionAndProof: altair.SignedContributionAndProof): Promise<number>;
96
98
  publishLightClientFinalityUpdate(update: LightClientFinalityUpdate): Promise<number>;
97
99
  publishLightClientOptimisticUpdate(update: LightClientOptimisticUpdate): Promise<number>;
100
+ publishSignedExecutionPayloadEnvelope(signedEnvelope: gloas.SignedExecutionPayloadEnvelope): Promise<number>;
98
101
 
99
102
  // Debug
100
103
  dumpGossipQueue(gossipType: GossipType): Promise<PendingGossipsubMessage[]>;