@lodestar/beacon-node 1.44.0-rc.2 → 1.44.0

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 (66) hide show
  1. package/lib/api/impl/config/constants.d.ts +4 -0
  2. package/lib/api/impl/config/constants.d.ts.map +1 -1
  3. package/lib/api/impl/config/constants.js +5 -1
  4. package/lib/api/impl/config/constants.js.map +1 -1
  5. package/lib/chain/blocks/importBlock.d.ts.map +1 -1
  6. package/lib/chain/blocks/importBlock.js +11 -0
  7. package/lib/chain/blocks/importBlock.js.map +1 -1
  8. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +1 -1
  9. package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
  10. package/lib/chain/chain.d.ts +2 -2
  11. package/lib/chain/chain.d.ts.map +1 -1
  12. package/lib/chain/chain.js +1 -1
  13. package/lib/chain/chain.js.map +1 -1
  14. package/lib/chain/errors/executionPayloadBid.d.ts +6 -0
  15. package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
  16. package/lib/chain/errors/executionPayloadBid.js +1 -0
  17. package/lib/chain/errors/executionPayloadBid.js.map +1 -1
  18. package/lib/chain/interface.d.ts +2 -2
  19. package/lib/chain/interface.d.ts.map +1 -1
  20. package/lib/chain/interface.js.map +1 -1
  21. package/lib/chain/prepareNextSlot.d.ts.map +1 -1
  22. package/lib/chain/prepareNextSlot.js +0 -9
  23. package/lib/chain/prepareNextSlot.js.map +1 -1
  24. package/lib/chain/produceBlock/produceBlockBody.d.ts +2 -2
  25. package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
  26. package/lib/chain/produceBlock/produceBlockBody.js +3 -3
  27. package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
  28. package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
  29. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +8 -2
  30. package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
  31. package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
  32. package/lib/chain/validation/executionPayloadBid.js +11 -0
  33. package/lib/chain/validation/executionPayloadBid.js.map +1 -1
  34. package/lib/chain/validation/executionPayloadEnvelope.js +1 -1
  35. package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
  36. package/lib/execution/engine/http.js +1 -1
  37. package/lib/execution/engine/http.js.map +1 -1
  38. package/lib/execution/engine/interface.d.ts +2 -2
  39. package/lib/execution/engine/interface.d.ts.map +1 -1
  40. package/lib/execution/engine/interface.js +6 -2
  41. package/lib/execution/engine/interface.js.map +1 -1
  42. package/lib/execution/engine/mock.d.ts.map +1 -1
  43. package/lib/execution/engine/mock.js +3 -5
  44. package/lib/execution/engine/mock.js.map +1 -1
  45. package/lib/execution/engine/types.d.ts +15 -7
  46. package/lib/execution/engine/types.d.ts.map +1 -1
  47. package/lib/execution/engine/types.js +47 -9
  48. package/lib/execution/engine/types.js.map +1 -1
  49. package/lib/network/gossip/topic.d.ts +10 -0
  50. package/lib/network/gossip/topic.d.ts.map +1 -1
  51. package/package.json +14 -14
  52. package/src/api/impl/config/constants.ts +8 -0
  53. package/src/chain/blocks/importBlock.ts +12 -0
  54. package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +1 -1
  55. package/src/chain/chain.ts +2 -3
  56. package/src/chain/errors/executionPayloadBid.ts +7 -0
  57. package/src/chain/interface.ts +1 -2
  58. package/src/chain/prepareNextSlot.ts +0 -10
  59. package/src/chain/produceBlock/produceBlockBody.ts +6 -6
  60. package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +8 -3
  61. package/src/chain/validation/executionPayloadBid.ts +12 -0
  62. package/src/chain/validation/executionPayloadEnvelope.ts +1 -1
  63. package/src/execution/engine/http.ts +1 -1
  64. package/src/execution/engine/interface.ts +12 -2
  65. package/src/execution/engine/mock.ts +6 -5
  66. package/src/execution/engine/types.ts +75 -14
@@ -217,16 +217,6 @@ export class PrepareNextSlotScheduler {
217
217
  });
218
218
  }
219
219
 
220
- if (ForkSeq[fork] >= ForkSeq.gloas) {
221
- // Cutoff = slot of the parent of the block we'll actually build on (post-reorg).
222
- // Steady state: cache holds just 2 entries — head (parent for next-slot production)
223
- // and head.parent (proposer-boost-reorg fallback). Anything older is evicted.
224
- const updatedHeadParent = this.chain.forkChoice.getBlockHexDefaultStatus(updatedHead.parentRoot);
225
- if (updatedHeadParent) {
226
- this.chain.seenPayloadEnvelopeInputCache.pruneBelowParent(updatedHeadParent);
227
- }
228
- }
229
-
230
220
  this.computeStateHashTreeRoot(updatedPrepareState, isEpochTransition);
231
221
 
232
222
  // If emitPayloadAttributes is true emit a SSE payloadAttributes event for
@@ -108,7 +108,7 @@ export type ProduceFullGloas = {
108
108
  type: BlockType.Full;
109
109
  fork: ForkPostGloas;
110
110
  executionPayload: ExecutionPayload<ForkPostGloas>;
111
- executionRequests: electra.ExecutionRequests;
111
+ executionRequests: gloas.ExecutionRequests;
112
112
  blobsBundle: BlobsBundle<ForkPostGloas>;
113
113
  cells: fulu.Cell[][];
114
114
  parentBlockRoot: Root;
@@ -228,7 +228,7 @@ export async function produceBlockBody<T extends BlockType>(
228
228
  );
229
229
  const parentExecutionRequests = isExtendingPayload
230
230
  ? await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot)
231
- : ssz.electra.ExecutionRequests.defaultValue();
231
+ : ssz.gloas.ExecutionRequests.defaultValue();
232
232
  executionPayloadValue = BigInt(builderBid.message.value) * GWEI_TO_WEI;
233
233
 
234
234
  const commonBlockBody = await commonBlockBodyPromise;
@@ -272,7 +272,7 @@ export async function produceBlockBody<T extends BlockType>(
272
272
 
273
273
  // Get execution payload from EL
274
274
  let parentBlockHash: Bytes32;
275
- let parentExecutionRequests: electra.ExecutionRequests;
275
+ let parentExecutionRequests: gloas.ExecutionRequests;
276
276
  // Apply parent payload once here as it's reused by EL prep and voluntary exit filtering below
277
277
  let stateAfterParentPayload: IBeaconStateViewBellatrix = currentState;
278
278
  // Spec: should_build_on_full(store, head). `parentBlock` is the proposer's head
@@ -285,7 +285,7 @@ export async function produceBlockBody<T extends BlockType>(
285
285
  stateAfterParentPayload = currentState.withParentPayloadApplied(parentExecutionRequests);
286
286
  } else {
287
287
  parentBlockHash = currentState.latestExecutionPayloadBid.parentBlockHash;
288
- parentExecutionRequests = ssz.electra.ExecutionRequests.defaultValue();
288
+ parentExecutionRequests = ssz.gloas.ExecutionRequests.defaultValue();
289
289
  }
290
290
  const prepareRes = await prepareExecutionPayload(
291
291
  this,
@@ -350,7 +350,7 @@ export async function produceBlockBody<T extends BlockType>(
350
350
  value: 0,
351
351
  executionPayment: 0,
352
352
  blobKzgCommitments: blobsBundle.commitments,
353
- executionRequestsRoot: ssz.electra.ExecutionRequests.hashTreeRoot(executionRequests),
353
+ executionRequestsRoot: ssz.gloas.ExecutionRequests.hashTreeRoot(executionRequests as gloas.ExecutionRequests),
354
354
  };
355
355
  const signedBid: gloas.SignedExecutionPayloadBid = {
356
356
  message: bid,
@@ -375,7 +375,7 @@ export async function produceBlockBody<T extends BlockType>(
375
375
  // Store execution payload data required to construct execution payload envelope later
376
376
  const gloasResult = produceResult as ProduceFullGloas;
377
377
  gloasResult.executionPayload = executionPayload as ExecutionPayload<ForkPostGloas>;
378
- gloasResult.executionRequests = executionRequests;
378
+ gloasResult.executionRequests = executionRequests as gloas.ExecutionRequests;
379
379
  gloasResult.blobsBundle = blobsBundle;
380
380
  gloasResult.cells = cells;
381
381
  gloasResult.parentBlockRoot = fromHex(parentBlock.blockRoot);
@@ -1,5 +1,5 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
- import {CheckpointWithHex, IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
2
+ import {CheckpointWithHex, IForkChoice, PayloadStatus, ProtoBlock} from "@lodestar/fork-choice";
3
3
  import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
4
4
  import {RootHex} from "@lodestar/types";
5
5
  import {Logger} from "@lodestar/utils";
@@ -154,9 +154,14 @@ export class SeenPayloadEnvelopeInput {
154
154
 
155
155
  pruneBelowParent(parentBlock: ProtoBlock): void {
156
156
  for (const block of this.forkChoice.getAllAncestorBlocks(parentBlock.blockRoot, parentBlock.payloadStatus)) {
157
- if (block.slot < parentBlock.slot) {
157
+ // Only evict once the payload is FULL (revealed/imported) — on an EMPTY/PENDING branch we may
158
+ // still need to download the FULL envelope (see #9475), and evicting would make payload-by-root
159
+ // sync throw "Missing PayloadEnvelopeInput for known block".
160
+ if (block.slot < parentBlock.slot && block.payloadStatus === PayloadStatus.FULL) {
158
161
  const input = this.payloadInputs.get(block.blockRoot);
159
- if (input) {
162
+ // ...and don't evict while columns are still being gathered: writeDataColumnsToDb awaits the
163
+ // same hasComputedAllData() before persisting. Such entries are pruned by a later call.
164
+ if (input?.hasComputedAllData()) {
160
165
  this.evictPayloadInput(input);
161
166
  this.logger?.verbose("SeenPayloadEnvelopeInput.pruneBelowParent deleted", {
162
167
  slot: block.slot,
@@ -1,4 +1,5 @@
1
1
  import {PublicKey} from "@chainsafe/blst";
2
+ import {PAYLOAD_BUILDER_VERSION} from "@lodestar/params";
2
3
  import {
3
4
  computeEpochAtSlot,
4
5
  createSingleSignatureSetFromComponents,
@@ -139,6 +140,17 @@ async function validateExecutionPayloadBid(
139
140
  });
140
141
  }
141
142
 
143
+ // [REJECT] The builder version is `PAYLOAD_BUILDER_VERSION` -- i.e.
144
+ // `state.builders[bid.builder_index].version == PAYLOAD_BUILDER_VERSION`.
145
+ if (builder.version !== PAYLOAD_BUILDER_VERSION) {
146
+ throw new ExecutionPayloadBidError(GossipAction.REJECT, {
147
+ code: ExecutionPayloadBidErrorCode.INVALID_BUILDER_VERSION,
148
+ builderIndex: bid.builderIndex,
149
+ version: builder.version,
150
+ expectedVersion: PAYLOAD_BUILDER_VERSION,
151
+ });
152
+ }
153
+
142
154
  // [REJECT] `bid.execution_payment` is zero.
143
155
  if (bid.executionPayment !== 0) {
144
156
  throw new ExecutionPayloadBidError(GossipAction.REJECT, {
@@ -106,7 +106,7 @@ async function validateExecutionPayloadEnvelope(
106
106
  }
107
107
 
108
108
  // [REJECT] `hash_tree_root(envelope.execution_requests) == bid.execution_requests_root`
109
- const requestsRoot = ssz.electra.ExecutionRequests.hashTreeRoot(envelope.executionRequests);
109
+ const requestsRoot = ssz.gloas.ExecutionRequests.hashTreeRoot(envelope.executionRequests);
110
110
  if (!byteArrayEquals(requestsRoot, payloadInput.getBid().executionRequestsRoot)) {
111
111
  throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
112
112
  code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_ROOT_MISMATCH,
@@ -244,7 +244,7 @@ export class ExecutionEngineHttp implements IExecutionEngine {
244
244
  if (executionRequests === undefined) {
245
245
  throw Error(`executionRequests required in notifyNewPayload for fork=${fork}`);
246
246
  }
247
- const serializedExecutionRequests = serializeExecutionRequests(executionRequests);
247
+ const serializedExecutionRequests = serializeExecutionRequests(fork, executionRequests);
248
248
  engineRequest = {
249
249
  method: ForkSeq[fork] >= ForkSeq.gloas ? "engine_newPayloadV5" : "engine_newPayloadV4",
250
250
  params: [
@@ -1,5 +1,7 @@
1
1
  import {routes} from "@lodestar/api";
2
2
  import {
3
+ BUILDER_DEPOSIT_REQUEST_TYPE,
4
+ BUILDER_EXIT_REQUEST_TYPE,
3
5
  CONSOLIDATION_REQUEST_TYPE,
4
6
  DEPOSIT_REQUEST_TYPE,
5
7
  ForkName,
@@ -51,10 +53,18 @@ export enum ExecutionEngineState {
51
53
  export type ExecutionRequestType =
52
54
  | typeof DEPOSIT_REQUEST_TYPE
53
55
  | typeof WITHDRAWAL_REQUEST_TYPE
54
- | typeof CONSOLIDATION_REQUEST_TYPE;
56
+ | typeof CONSOLIDATION_REQUEST_TYPE
57
+ | typeof BUILDER_DEPOSIT_REQUEST_TYPE
58
+ | typeof BUILDER_EXIT_REQUEST_TYPE;
55
59
 
56
60
  export function isExecutionRequestType(type: number): type is ExecutionRequestType {
57
- return type === DEPOSIT_REQUEST_TYPE || type === WITHDRAWAL_REQUEST_TYPE || type === CONSOLIDATION_REQUEST_TYPE;
61
+ return (
62
+ type === DEPOSIT_REQUEST_TYPE ||
63
+ type === WITHDRAWAL_REQUEST_TYPE ||
64
+ type === CONSOLIDATION_REQUEST_TYPE ||
65
+ type === BUILDER_DEPOSIT_REQUEST_TYPE ||
66
+ type === BUILDER_EXIT_REQUEST_TYPE
67
+ );
58
68
  }
59
69
 
60
70
  export type ExecutePayloadResponse =
@@ -393,11 +393,12 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend {
393
393
  blobs,
394
394
  proofs,
395
395
  }),
396
- executionRequests: serializeExecutionRequests({
397
- deposits: ssz.electra.DepositRequests.defaultValue(),
398
- withdrawals: ssz.electra.WithdrawalRequests.defaultValue(),
399
- consolidations: ssz.electra.ConsolidationRequests.defaultValue(),
400
- }),
396
+ executionRequests: serializeExecutionRequests(
397
+ fork,
398
+ ForkSeq[fork] >= ForkSeq.gloas
399
+ ? ssz.gloas.ExecutionRequests.defaultValue()
400
+ : ssz.electra.ExecutionRequests.defaultValue()
401
+ ),
401
402
  });
402
403
 
403
404
  // IF the payload is deemed VALID and the build process has begun
@@ -1,4 +1,6 @@
1
1
  import {
2
+ BUILDER_DEPOSIT_REQUEST_TYPE,
3
+ BUILDER_EXIT_REQUEST_TYPE,
2
4
  BYTES_PER_FIELD_ELEMENT,
3
5
  BYTES_PER_LOGS_BLOOM,
4
6
  CELLS_PER_EXT_BLOB,
@@ -205,16 +207,27 @@ export type WithdrawalRpc = {
205
207
  };
206
208
 
207
209
  /**
208
- * ExecutionRequestsRpc only holds at most 3 elements and no repeated type:
209
- * - ssz'ed DepositRequests
210
- * - ssz'ed WithdrawalRequests
211
- * - ssz'ed ConsolidationRequests
210
+ * ExecutionRequestsRpc holds at most 5 elements and no repeated type, ordered by
211
+ * the single-byte request type prefix:
212
+ * - ssz'ed DepositRequests (0x00)
213
+ * - ssz'ed WithdrawalRequests (0x01)
214
+ * - ssz'ed ConsolidationRequests (0x02)
215
+ * - ssz'ed BuilderDepositRequests (0x03)
216
+ * - ssz'ed BuilderExitRequests (0x04)
212
217
  */
213
- export type ExecutionRequestsRpc = (DepositRequestsRpc | WithdrawalRequestsRpc | ConsolidationRequestsRpc)[];
218
+ export type ExecutionRequestsRpc = (
219
+ | DepositRequestsRpc
220
+ | WithdrawalRequestsRpc
221
+ | ConsolidationRequestsRpc
222
+ | BuilderDepositRequestsRpc
223
+ | BuilderExitRequestsRpc
224
+ )[];
214
225
 
215
226
  export type DepositRequestsRpc = DATA;
216
227
  export type WithdrawalRequestsRpc = DATA;
217
228
  export type ConsolidationRequestsRpc = DATA;
229
+ export type BuilderDepositRequestsRpc = DATA;
230
+ export type BuilderExitRequestsRpc = DATA;
218
231
 
219
232
  export type BlobAndProofRpc = {
220
233
  blob: DATA;
@@ -339,7 +352,7 @@ export function parseExecutionPayload(
339
352
  data = response.executionPayload;
340
353
  blobsBundle = response.blobsBundle ? parseBlobsBundle(response.blobsBundle) : undefined;
341
354
  executionRequests = response.executionRequests
342
- ? deserializeExecutionRequests(response.executionRequests)
355
+ ? deserializeExecutionRequests(fork, response.executionRequests)
343
356
  : undefined;
344
357
  shouldOverrideBuilder = response.shouldOverrideBuilder ?? false;
345
358
  } else {
@@ -526,13 +539,36 @@ function deserializeConsolidationRequests(serialized: ConsolidationRequestsRpc):
526
539
  return ssz.electra.ConsolidationRequests.deserialize(dataToBytes(serialized, null));
527
540
  }
528
541
 
542
+ function serializeBuilderDepositRequests(
543
+ builderDepositRequests: gloas.BuilderDepositRequests
544
+ ): BuilderDepositRequestsRpc {
545
+ const requestsBytes = ssz.gloas.BuilderDepositRequests.serialize(builderDepositRequests);
546
+ return bytesToData(prefixRequests(requestsBytes, BUILDER_DEPOSIT_REQUEST_TYPE));
547
+ }
548
+
549
+ function deserializeBuilderDepositRequests(serialized: BuilderDepositRequestsRpc): gloas.BuilderDepositRequests {
550
+ return ssz.gloas.BuilderDepositRequests.deserialize(dataToBytes(serialized, null));
551
+ }
552
+
553
+ function serializeBuilderExitRequests(builderExitRequests: gloas.BuilderExitRequests): BuilderExitRequestsRpc {
554
+ const requestsBytes = ssz.gloas.BuilderExitRequests.serialize(builderExitRequests);
555
+ return bytesToData(prefixRequests(requestsBytes, BUILDER_EXIT_REQUEST_TYPE));
556
+ }
557
+
558
+ function deserializeBuilderExitRequests(serialized: BuilderExitRequestsRpc): gloas.BuilderExitRequests {
559
+ return ssz.gloas.BuilderExitRequests.deserialize(dataToBytes(serialized, null));
560
+ }
561
+
529
562
  /**
530
563
  * This is identical to get_execution_requests_list in
531
564
  * https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/electra/beacon-chain.md#new-get_execution_requests_list
565
+ *
566
+ * Gloas extends the list with builder deposits (0x03) and builder exits (0x04) per
567
+ * https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.11/specs/gloas/beacon-chain.md#modified-get_execution_requests_list
532
568
  */
533
- export function serializeExecutionRequests(executionRequests: ExecutionRequests): ExecutionRequestsRpc {
569
+ export function serializeExecutionRequests(fork: ForkName, executionRequests: ExecutionRequests): ExecutionRequestsRpc {
534
570
  const {deposits, withdrawals, consolidations} = executionRequests;
535
- const result = [];
571
+ const result: ExecutionRequestsRpc = [];
536
572
 
537
573
  if (deposits.length !== 0) {
538
574
  result.push(serializeDepositRequests(deposits));
@@ -546,15 +582,26 @@ export function serializeExecutionRequests(executionRequests: ExecutionRequests)
546
582
  result.push(serializeConsolidationRequests(consolidations));
547
583
  }
548
584
 
585
+ if (ForkSeq[fork] >= ForkSeq.gloas) {
586
+ const {builderDeposits, builderExits} = executionRequests as gloas.ExecutionRequests;
587
+
588
+ if (builderDeposits.length !== 0) {
589
+ result.push(serializeBuilderDepositRequests(builderDeposits));
590
+ }
591
+
592
+ if (builderExits.length !== 0) {
593
+ result.push(serializeBuilderExitRequests(builderExits));
594
+ }
595
+ }
596
+
549
597
  return result;
550
598
  }
551
599
 
552
- export function deserializeExecutionRequests(serialized: ExecutionRequestsRpc): ExecutionRequests {
553
- const result: ExecutionRequests = {
554
- deposits: [],
555
- withdrawals: [],
556
- consolidations: [],
557
- };
600
+ export function deserializeExecutionRequests(fork: ForkName, serialized: ExecutionRequestsRpc): ExecutionRequests {
601
+ const result: ExecutionRequests =
602
+ ForkSeq[fork] >= ForkSeq.gloas
603
+ ? {deposits: [], withdrawals: [], consolidations: [], builderDeposits: [], builderExits: []}
604
+ : {deposits: [], withdrawals: [], consolidations: []};
558
605
 
559
606
  if (serialized.length === 0) {
560
607
  return result;
@@ -601,6 +648,20 @@ export function deserializeExecutionRequests(serialized: ExecutionRequestsRpc):
601
648
  result.consolidations = deserializeConsolidationRequests(requests);
602
649
  break;
603
650
  }
651
+ case BUILDER_DEPOSIT_REQUEST_TYPE: {
652
+ if (ForkSeq[fork] < ForkSeq.gloas) {
653
+ throw Error(`Builder deposit request is not supported pre-gloas fork=${fork}`);
654
+ }
655
+ (result as gloas.ExecutionRequests).builderDeposits = deserializeBuilderDepositRequests(requests);
656
+ break;
657
+ }
658
+ case BUILDER_EXIT_REQUEST_TYPE: {
659
+ if (ForkSeq[fork] < ForkSeq.gloas) {
660
+ throw Error(`Builder exit request is not supported pre-gloas fork=${fork}`);
661
+ }
662
+ (result as gloas.ExecutionRequests).builderExits = deserializeBuilderExitRequests(requests);
663
+ break;
664
+ }
604
665
  }
605
666
  prevRequestType = currentRequestType;
606
667
  }