@lodestar/state-transition 1.42.0 → 1.43.0-dev.07875b3e0c

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 (98) hide show
  1. package/lib/block/index.d.ts +2 -2
  2. package/lib/block/index.d.ts.map +1 -1
  3. package/lib/block/index.js +11 -4
  4. package/lib/block/index.js.map +1 -1
  5. package/lib/block/processDepositRequest.d.ts +11 -2
  6. package/lib/block/processDepositRequest.d.ts.map +1 -1
  7. package/lib/block/processDepositRequest.js +34 -4
  8. package/lib/block/processDepositRequest.js.map +1 -1
  9. package/lib/block/processParentExecutionPayload.d.ts +20 -0
  10. package/lib/block/processParentExecutionPayload.d.ts.map +1 -0
  11. package/lib/block/processParentExecutionPayload.js +100 -0
  12. package/lib/block/processParentExecutionPayload.js.map +1 -0
  13. package/lib/block/processWithdrawals.d.ts.map +1 -1
  14. package/lib/block/processWithdrawals.js +12 -4
  15. package/lib/block/processWithdrawals.js.map +1 -1
  16. package/lib/cache/epochCache.d.ts +3 -1
  17. package/lib/cache/epochCache.d.ts.map +1 -1
  18. package/lib/cache/epochCache.js +31 -13
  19. package/lib/cache/epochCache.js.map +1 -1
  20. package/lib/cache/epochTransitionCache.d.ts +5 -0
  21. package/lib/cache/epochTransitionCache.d.ts.map +1 -1
  22. package/lib/cache/epochTransitionCache.js +1 -0
  23. package/lib/cache/epochTransitionCache.js.map +1 -1
  24. package/lib/epoch/index.d.ts +3 -1
  25. package/lib/epoch/index.d.ts.map +1 -1
  26. package/lib/epoch/index.js +8 -1
  27. package/lib/epoch/index.js.map +1 -1
  28. package/lib/epoch/processPtcWindow.d.ts +11 -0
  29. package/lib/epoch/processPtcWindow.d.ts.map +1 -0
  30. package/lib/epoch/processPtcWindow.js +28 -0
  31. package/lib/epoch/processPtcWindow.js.map +1 -0
  32. package/lib/index.d.ts +4 -2
  33. package/lib/index.d.ts.map +1 -1
  34. package/lib/index.js +4 -1
  35. package/lib/index.js.map +1 -1
  36. package/lib/signatureSets/executionPayloadEnvelope.d.ts.map +1 -1
  37. package/lib/signatureSets/executionPayloadEnvelope.js +5 -1
  38. package/lib/signatureSets/executionPayloadEnvelope.js.map +1 -1
  39. package/lib/signatureSets/voluntaryExits.d.ts +2 -2
  40. package/lib/signatureSets/voluntaryExits.d.ts.map +1 -1
  41. package/lib/signatureSets/voluntaryExits.js +4 -0
  42. package/lib/signatureSets/voluntaryExits.js.map +1 -1
  43. package/lib/slot/upgradeStateToGloas.d.ts.map +1 -1
  44. package/lib/slot/upgradeStateToGloas.js +3 -1
  45. package/lib/slot/upgradeStateToGloas.js.map +1 -1
  46. package/lib/stateTransition.d.ts +1 -2
  47. package/lib/stateTransition.d.ts.map +1 -1
  48. package/lib/stateTransition.js +1 -2
  49. package/lib/stateTransition.js.map +1 -1
  50. package/lib/stateView/beaconStateView.d.ts +14 -12
  51. package/lib/stateView/beaconStateView.d.ts.map +1 -1
  52. package/lib/stateView/beaconStateView.js +52 -43
  53. package/lib/stateView/beaconStateView.js.map +1 -1
  54. package/lib/stateView/interface.d.ts +109 -54
  55. package/lib/stateView/interface.d.ts.map +1 -1
  56. package/lib/stateView/interface.js +22 -1
  57. package/lib/stateView/interface.js.map +1 -1
  58. package/lib/util/attestation.d.ts +12 -1
  59. package/lib/util/attestation.d.ts.map +1 -1
  60. package/lib/util/attestation.js +23 -8
  61. package/lib/util/attestation.js.map +1 -1
  62. package/lib/util/computeAnchorCheckpoint.d.ts +1 -1
  63. package/lib/util/computeAnchorCheckpoint.d.ts.map +1 -1
  64. package/lib/util/computeAnchorCheckpoint.js +6 -19
  65. package/lib/util/computeAnchorCheckpoint.js.map +1 -1
  66. package/lib/util/execution.d.ts +4 -2
  67. package/lib/util/execution.d.ts.map +1 -1
  68. package/lib/util/execution.js +7 -0
  69. package/lib/util/execution.js.map +1 -1
  70. package/lib/util/gloas.d.ts +7 -1
  71. package/lib/util/gloas.d.ts.map +1 -1
  72. package/lib/util/gloas.js +27 -1
  73. package/lib/util/gloas.js.map +1 -1
  74. package/package.json +8 -8
  75. package/src/block/index.ts +12 -4
  76. package/src/block/processDepositRequest.ts +50 -5
  77. package/src/block/processParentExecutionPayload.ts +116 -0
  78. package/src/block/processWithdrawals.ts +12 -4
  79. package/src/cache/epochCache.ts +32 -30
  80. package/src/cache/epochTransitionCache.ts +7 -0
  81. package/src/epoch/index.ts +9 -0
  82. package/src/epoch/processPtcWindow.ts +38 -0
  83. package/src/index.ts +20 -2
  84. package/src/signatureSets/executionPayloadEnvelope.ts +6 -2
  85. package/src/signatureSets/voluntaryExits.ts +5 -2
  86. package/src/slot/upgradeStateToGloas.ts +5 -1
  87. package/src/stateTransition.ts +1 -2
  88. package/src/stateView/beaconStateView.ts +71 -56
  89. package/src/stateView/interface.ts +161 -79
  90. package/src/util/attestation.ts +37 -8
  91. package/src/util/computeAnchorCheckpoint.ts +6 -19
  92. package/src/util/execution.ts +11 -1
  93. package/src/util/gloas.ts +49 -1
  94. package/lib/block/processExecutionPayloadEnvelope.d.ts +0 -9
  95. package/lib/block/processExecutionPayloadEnvelope.d.ts.map +0 -1
  96. package/lib/block/processExecutionPayloadEnvelope.js +0 -102
  97. package/lib/block/processExecutionPayloadEnvelope.js.map +0 -1
  98. package/src/block/processExecutionPayloadEnvelope.ts +0 -170
@@ -3,7 +3,7 @@ import {BeaconConfig} from "@lodestar/config";
3
3
  import {ForkSeq} from "@lodestar/params";
4
4
  import {SignedBeaconBlock, Slot, phase0, ssz} from "@lodestar/types";
5
5
  import {PubkeyCache} from "../cache/pubkeyCache.js";
6
- import {IBeaconStateView} from "../stateView/interface.js";
6
+ import {IBeaconStateView, IBeaconStateViewGloas, isStatePostGloas} from "../stateView/interface.js";
7
7
  import {
8
8
  ISignatureSet,
9
9
  SignatureSetType,
@@ -34,6 +34,9 @@ export function getVoluntaryExitSignatureSet(
34
34
  const fork = config.getForkSeq(state.slot);
35
35
 
36
36
  if (fork >= ForkSeq.gloas && isBuilderVoluntaryExit(signedVoluntaryExit)) {
37
+ if (!isStatePostGloas(state)) {
38
+ throw new Error(`Expected gloas+ state for builder voluntary exit signature, got fork=${state.forkName}`);
39
+ }
37
40
  return getBuilderVoluntaryExitSignatureSet(config, state, signedVoluntaryExit);
38
41
  }
39
42
 
@@ -68,7 +71,7 @@ export function getValidatorVoluntaryExitSignatureSet(
68
71
 
69
72
  export function getBuilderVoluntaryExitSignatureSet(
70
73
  config: BeaconConfig,
71
- state: IBeaconStateView,
74
+ state: IBeaconStateViewGloas,
72
75
  signedVoluntaryExit: phase0.SignedVoluntaryExit
73
76
  ): ISignatureSet {
74
77
  const messageSlot = computeStartSlotAtEpoch(signedVoluntaryExit.message.epoch);
@@ -5,7 +5,7 @@ import {isValidDepositSignature} from "../block/processDeposit.js";
5
5
  import {applyDepositForBuilder} from "../block/processDepositRequest.js";
6
6
  import {getCachedBeaconState} from "../cache/stateCache.js";
7
7
  import {CachedBeaconStateFulu, CachedBeaconStateGloas} from "../types.js";
8
- import {isBuilderWithdrawalCredential} from "../util/gloas.js";
8
+ import {initializePtcWindow, isBuilderWithdrawalCredential} from "../util/gloas.js";
9
9
  import {isValidatorKnown} from "../util/index.js";
10
10
 
11
11
  /**
@@ -48,6 +48,9 @@ export function upgradeStateToGloas(stateFulu: CachedBeaconStateFulu): CachedBea
48
48
  stateGloasView.currentSyncCommittee = stateGloasCloned.currentSyncCommittee;
49
49
  stateGloasView.nextSyncCommittee = stateGloasCloned.nextSyncCommittee;
50
50
  stateGloasView.latestExecutionPayloadBid.blockHash = stateFulu.latestExecutionPayloadHeader.blockHash;
51
+ stateGloasView.latestExecutionPayloadBid.executionRequestsRoot = ssz.electra.ExecutionRequests.hashTreeRoot(
52
+ ssz.electra.ExecutionRequests.defaultValue()
53
+ );
51
54
  stateGloasView.nextWithdrawalIndex = stateGloasCloned.nextWithdrawalIndex;
52
55
  stateGloasView.nextWithdrawalValidatorIndex = stateGloasCloned.nextWithdrawalValidatorIndex;
53
56
  stateGloasView.historicalSummaries = stateGloasCloned.historicalSummaries;
@@ -61,6 +64,7 @@ export function upgradeStateToGloas(stateFulu: CachedBeaconStateFulu): CachedBea
61
64
  stateGloasView.pendingPartialWithdrawals = stateGloasCloned.pendingPartialWithdrawals;
62
65
  stateGloasView.pendingConsolidations = stateGloasCloned.pendingConsolidations;
63
66
  stateGloasView.proposerLookahead = stateGloasCloned.proposerLookahead;
67
+ stateGloasView.ptcWindow = ssz.gloas.PtcWindow.toViewDU(initializePtcWindow(stateFulu));
64
68
 
65
69
  for (let i = 0; i < SLOTS_PER_HISTORICAL_ROOT; i++) {
66
70
  stateGloasView.executionPayloadAvailability.set(i, true);
@@ -76,7 +76,6 @@ export enum StateHashTreeRootSource {
76
76
  prepareNextEpoch = "prepare_next_epoch",
77
77
  regenState = "regen_state",
78
78
  computeNewStateRoot = "compute_new_state_root",
79
- computeEnvelopeStateRoot = "compute_envelope_state_root",
80
79
  }
81
80
 
82
81
  /**
@@ -283,7 +282,7 @@ function processSlotsWithTransientCache(
283
282
  {
284
283
  const timer = metrics?.epochTransitionStepTime.startTimer({step: EpochTransitionStep.finalProcessEpoch});
285
284
  // last step to prepare epoch data that depends on the upgraded state, for example proposerLookahead of BeaconStateFulu
286
- postState.epochCtx.finalProcessEpoch(postState);
285
+ postState.epochCtx.finalProcessEpoch(postState, epochTransitionCache);
287
286
  timer?.();
288
287
  }
289
288
 
@@ -1,7 +1,7 @@
1
1
  import {CompactMultiProof, ProofType, Tree, createProof} from "@chainsafe/persistent-merkle-tree";
2
2
  import {BitArray, ByteViews} from "@chainsafe/ssz";
3
3
  import {BeaconConfig} from "@lodestar/config";
4
- import {ForkSeq, SLOTS_PER_HISTORICAL_ROOT, isForkPostGloas} from "@lodestar/params";
4
+ import {ForkName, ForkSeq, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
5
5
  import {
6
6
  BeaconBlock,
7
7
  BeaconState,
@@ -28,8 +28,7 @@ import {
28
28
  rewards,
29
29
  } from "@lodestar/types";
30
30
  import {Checkpoint, Fork} from "@lodestar/types/phase0";
31
- import {processExecutionPayloadEnvelope} from "../block/index.js";
32
- import {ProcessExecutionPayloadEnvelopeOpts} from "../block/processExecutionPayloadEnvelope.js";
31
+ import {applyParentExecutionPayload} from "../block/processParentExecutionPayload.js";
33
32
  import {VoluntaryExitValidity, getVoluntaryExitValidity} from "../block/processVoluntaryExit.js";
34
33
  import {getExpectedWithdrawals} from "../block/processWithdrawals.js";
35
34
  import {EffectiveBalanceIncrements} from "../cache/effectiveBalanceIncrements.js";
@@ -59,14 +58,19 @@ import {getBlockRootAtSlot} from "../util/blockRoot.js";
59
58
  import {computeAnchorCheckpoint} from "../util/computeAnchorCheckpoint.js";
60
59
  import {computeEpochAtSlot, computeStartSlotAtEpoch} from "../util/epoch.js";
61
60
  import {EpochShuffling} from "../util/epochShuffling.js";
62
- import {isExecutionEnabled, isExecutionStateType, isMergeTransitionComplete} from "../util/execution.js";
61
+ import {
62
+ isExecutionEnabled,
63
+ isExecutionStateType,
64
+ isGloasStateType,
65
+ isMergeTransitionComplete,
66
+ } from "../util/execution.js";
63
67
  import {canBuilderCoverBid} from "../util/gloas.js";
64
68
  import {loadState} from "../util/loadState/loadState.js";
65
69
  import {getRandaoMix} from "../util/seed.js";
66
70
  import {getLatestWeakSubjectivityCheckpointEpoch} from "../util/weakSubjectivity.js";
67
- import {IBeaconStateView} from "./interface.js";
71
+ import {IBeaconStateView, IBeaconStateViewLatestFork} from "./interface.js";
68
72
 
69
- export class BeaconStateView implements IBeaconStateView {
73
+ export class BeaconStateView implements IBeaconStateViewLatestFork {
70
74
  private readonly config: BeaconConfig;
71
75
  // Cached values extracted from the tree
72
76
  // phase0
@@ -79,8 +83,6 @@ export class BeaconStateView implements IBeaconStateView {
79
83
  private _currentEpochParticipation: Uint8Array | null = null;
80
84
  // bellatrix
81
85
  private _latestExecutionPayloadHeader: ExecutionPayloadHeader | null = null;
82
- // Caches the cross-fork latestBlockHash value
83
- private _latestBlockHash: Bytes32 | null = null;
84
86
  // capella
85
87
  private _historicalSummaries: capella.HistoricalSummaries | null = null;
86
88
  // electra
@@ -92,6 +94,7 @@ export class BeaconStateView implements IBeaconStateView {
92
94
  // gloas
93
95
  private _executionPayloadAvailability: BitArray | null = null;
94
96
  private _latestExecutionPayloadBid: ExecutionPayloadBid | null = null;
97
+ private _payloadExpectedWithdrawals: capella.Withdrawal[] | null = null;
95
98
 
96
99
  constructor(readonly cachedState: CachedBeaconStateAllForks) {
97
100
  this.config = cachedState.config;
@@ -99,6 +102,10 @@ export class BeaconStateView implements IBeaconStateView {
99
102
 
100
103
  // phase0
101
104
 
105
+ get forkName(): ForkName {
106
+ return this.config.getForkName(this.cachedState.slot);
107
+ }
108
+
102
109
  get slot(): number {
103
110
  return this.cachedState.slot;
104
111
  }
@@ -208,9 +215,13 @@ export class BeaconStateView implements IBeaconStateView {
208
215
  // bellatrix
209
216
 
210
217
  get latestExecutionPayloadHeader(): ExecutionPayloadHeader {
211
- if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.bellatrix) {
218
+ const forkSeq = this.config.getForkSeq(this.cachedState.slot);
219
+ if (forkSeq < ForkSeq.bellatrix) {
212
220
  throw new Error("latestExecutionPayloadHeader is not available before Bellatrix");
213
221
  }
222
+ if (forkSeq >= ForkSeq.gloas) {
223
+ throw new Error("latestExecutionPayloadHeader is not available after Gloas");
224
+ }
214
225
 
215
226
  if (this._latestExecutionPayloadHeader === null) {
216
227
  this._latestExecutionPayloadHeader = (
@@ -221,30 +232,6 @@ export class BeaconStateView implements IBeaconStateView {
221
232
  return this._latestExecutionPayloadHeader;
222
233
  }
223
234
 
224
- /**
225
- * Cross-fork accessor for the execution block hash of the most recently included payload.
226
- * Pre-gloas: reads from latestExecutionPayloadHeader.blockHash.
227
- * Gloas+: reads the dedicated latestBlockHash field (EIP-7732).
228
- */
229
- get latestBlockHash(): Bytes32 {
230
- const forkSeq = this.config.getForkSeq(this.cachedState.slot);
231
- if (forkSeq < ForkSeq.bellatrix) {
232
- throw new Error("latestBlockHash is not available before Bellatrix");
233
- }
234
-
235
- if (this._latestBlockHash === null) {
236
- if (forkSeq >= ForkSeq.gloas) {
237
- this._latestBlockHash = (this.cachedState as CachedBeaconStateGloas).latestBlockHash;
238
- } else {
239
- this._latestBlockHash = (
240
- this.cachedState as CachedBeaconStateExecutions
241
- ).latestExecutionPayloadHeader.blockHash;
242
- }
243
- }
244
-
245
- return this._latestBlockHash;
246
- }
247
-
248
235
  /**
249
236
  * The execution block number of the most recently included payload.
250
237
  * Named payloadBlockNumber (not latestBlockNumber) to mirror ExecutionPayloadHeader.blockNumber pre-gloas.
@@ -355,9 +342,16 @@ export class BeaconStateView implements IBeaconStateView {
355
342
 
356
343
  // gloas
357
344
 
345
+ get latestBlockHash(): Bytes32 {
346
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
347
+ throw new Error("latestBlockHash is not available before Gloas");
348
+ }
349
+ return (this.cachedState as CachedBeaconStateGloas).latestBlockHash;
350
+ }
351
+
358
352
  get executionPayloadAvailability(): BitArray {
359
353
  if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
360
- throw new Error("executionPayloadAvailability is not available before GLOAS");
354
+ throw new Error("executionPayloadAvailability is not available before Gloas");
361
355
  }
362
356
 
363
357
  if (this._executionPayloadAvailability === null) {
@@ -371,7 +365,7 @@ export class BeaconStateView implements IBeaconStateView {
371
365
 
372
366
  get latestExecutionPayloadBid(): ExecutionPayloadBid {
373
367
  if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
374
- throw new Error("latestExecutionPayloadBid is not available before GLOAS");
368
+ throw new Error("latestExecutionPayloadBid is not available before Gloas");
375
369
  }
376
370
 
377
371
  if (this._latestExecutionPayloadBid === null) {
@@ -382,9 +376,22 @@ export class BeaconStateView implements IBeaconStateView {
382
376
  return this._latestExecutionPayloadBid;
383
377
  }
384
378
 
379
+ get payloadExpectedWithdrawals(): capella.Withdrawal[] {
380
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
381
+ throw new Error("payloadExpectedWithdrawals is not available before Gloas");
382
+ }
383
+
384
+ if (this._payloadExpectedWithdrawals === null) {
385
+ this._payloadExpectedWithdrawals = (
386
+ this.cachedState as CachedBeaconStateGloas
387
+ ).payloadExpectedWithdrawals.toValue();
388
+ }
389
+ return this._payloadExpectedWithdrawals;
390
+ }
391
+
385
392
  getBuilder(index: BuilderIndex): gloas.Builder {
386
393
  if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
387
- throw new Error("Builders are not supported before GLOAS");
394
+ throw new Error("Builders are not supported before Gloas");
388
395
  }
389
396
 
390
397
  return (this.cachedState as CachedBeaconStateGloas).builders.getReadonly(index);
@@ -392,7 +399,7 @@ export class BeaconStateView implements IBeaconStateView {
392
399
 
393
400
  canBuilderCoverBid(builderIndex: BuilderIndex, bidAmount: number): boolean {
394
401
  if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
395
- throw new Error("Builders are not supported before GLOAS");
402
+ throw new Error("Builders are not supported before Gloas");
396
403
  }
397
404
 
398
405
  return canBuilderCoverBid(this.cachedState as CachedBeaconStateGloas, builderIndex, bidAmount);
@@ -404,7 +411,7 @@ export class BeaconStateView implements IBeaconStateView {
404
411
  */
405
412
  getIndexInPayloadTimelinessCommittee(validatorIndex: ValidatorIndex, slot: Slot): number {
406
413
  if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
407
- throw new Error("PTC committees are not supported before GLOAS");
414
+ throw new Error("PTC committees are not supported before Gloas");
408
415
  }
409
416
 
410
417
  const ptcCommittee = (this.cachedState as CachedBeaconStateGloas).epochCtx.getPayloadTimelinessCommittee(slot);
@@ -571,7 +578,10 @@ export class BeaconStateView implements IBeaconStateView {
571
578
  }
572
579
 
573
580
  get isMergeTransitionComplete(): boolean {
574
- return isExecutionStateType(this.cachedState) && isMergeTransitionComplete(this.cachedState);
581
+ return (
582
+ (isExecutionStateType(this.cachedState) || isGloasStateType(this.cachedState)) &&
583
+ isMergeTransitionComplete(this.cachedState)
584
+ );
575
585
  }
576
586
 
577
587
  // Block production
@@ -693,7 +703,11 @@ export class BeaconStateView implements IBeaconStateView {
693
703
 
694
704
  // Serialization
695
705
 
696
- loadOtherState(stateBytes: Uint8Array, seedValidatorsBytes?: Uint8Array): IBeaconStateView {
706
+ loadOtherState(
707
+ stateBytes: Uint8Array,
708
+ seedValidatorsBytes?: Uint8Array,
709
+ opts?: {preloadValidatorsAndBalances?: boolean}
710
+ ): IBeaconStateView {
697
711
  const {state} = loadState(this.config, this.cachedState, stateBytes, seedValidatorsBytes);
698
712
 
699
713
  const cachedState = createCachedBeaconState(
@@ -708,9 +722,10 @@ export class BeaconStateView implements IBeaconStateView {
708
722
  }
709
723
  );
710
724
 
711
- // load all cache in order for consumers (usually regen.getState()) to process blocks faster
712
- cachedState.validators.getAllReadonlyValues();
713
- cachedState.balances.getAll();
725
+ if (opts?.preloadValidatorsAndBalances) {
726
+ cachedState.validators.getAllReadonlyValues();
727
+ cachedState.balances.getAll();
728
+ }
714
729
 
715
730
  return new BeaconStateView(cachedState);
716
731
  }
@@ -768,19 +783,19 @@ export class BeaconStateView implements IBeaconStateView {
768
783
  return new BeaconStateView(newState);
769
784
  }
770
785
 
771
- processExecutionPayloadEnvelope(
772
- signedEnvelope: gloas.SignedExecutionPayloadEnvelope,
773
- opts?: ProcessExecutionPayloadEnvelopeOpts
774
- ): BeaconStateView {
775
- const fork = this.config.getForkName(this.cachedState.slot);
776
- if (!isForkPostGloas(fork)) {
777
- throw Error(`processExecutionPayloadEnvelope is only available for gloas+ forks, got fork=${fork}`);
786
+ /**
787
+ * Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.5/specs/gloas/validator.md#executionpayload
788
+ */
789
+ getExpectedWithdrawalsForFullParent(envelope: gloas.SignedExecutionPayloadEnvelope): capella.Withdrawal[] {
790
+ const fork = this.config.getForkSeq(this.cachedState.slot);
791
+ if (fork < ForkSeq.gloas) {
792
+ throw new Error("getExpectedWithdrawalsForFullParent is not available before Gloas");
778
793
  }
779
- const postPayloadState = processExecutionPayloadEnvelope(
780
- this.cachedState as CachedBeaconStateGloas,
781
- signedEnvelope,
782
- opts
783
- );
784
- return new BeaconStateView(postPayloadState);
794
+ // Make a copy of the state to avoid mutability issues
795
+ const stateCopy = this.cachedState.clone(true) as CachedBeaconStateGloas;
796
+ // Apply parent payload before computing withdrawals
797
+ applyParentExecutionPayload(stateCopy, envelope.message.executionRequests);
798
+
799
+ return getExpectedWithdrawals(fork, stateCopy).expectedWithdrawals;
785
800
  }
786
801
  }
@@ -1,5 +1,22 @@
1
1
  import {CompactMultiProof} from "@chainsafe/persistent-merkle-tree";
2
2
  import {BitArray, ByteViews} from "@chainsafe/ssz";
3
+ import {
4
+ ForkName,
5
+ ForkPostAltair,
6
+ ForkPostBellatrix,
7
+ ForkPostCapella,
8
+ ForkPostDeneb,
9
+ ForkPostElectra,
10
+ ForkPostFulu,
11
+ ForkPostGloas,
12
+ isForkPostAltair,
13
+ isForkPostBellatrix,
14
+ isForkPostCapella,
15
+ isForkPostDeneb,
16
+ isForkPostElectra,
17
+ isForkPostFulu,
18
+ isForkPostGloas,
19
+ } from "@lodestar/params";
3
20
  import {
4
21
  BeaconBlock,
5
22
  BeaconState,
@@ -24,7 +41,6 @@ import {
24
41
  rewards,
25
42
  } from "@lodestar/types";
26
43
  import {Checkpoint, Fork} from "@lodestar/types/phase0";
27
- import {ProcessExecutionPayloadEnvelopeOpts} from "../block/processExecutionPayloadEnvelope.js";
28
44
  import {VoluntaryExitValidity} from "../block/processVoluntaryExit.js";
29
45
  import {EffectiveBalanceIncrements} from "../cache/effectiveBalanceIncrements.js";
30
46
  import {EpochTransitionCacheOpts} from "../cache/epochTransitionCache.js";
@@ -41,6 +57,7 @@ export interface IBeaconStateView {
41
57
  // State access
42
58
 
43
59
  // phase0
60
+ forkName: ForkName;
44
61
  slot: Slot;
45
62
  fork: Fork;
46
63
  epoch: Epoch;
@@ -56,50 +73,6 @@ export interface IBeaconStateView {
56
73
  getStateRootAtSlot(slot: Slot): Root;
57
74
  getRandaoMix(epoch: Epoch): Bytes32;
58
75
 
59
- // altair
60
- previousEpochParticipation: Uint8Array;
61
- currentEpochParticipation: Uint8Array;
62
- getPreviousEpochParticipation(validatorIndex: ValidatorIndex): number;
63
- getCurrentEpochParticipation(validatorIndex: ValidatorIndex): number;
64
-
65
- // bellatrix
66
- latestExecutionPayloadHeader: ExecutionPayloadHeader;
67
- /**
68
- * Cross-fork accessor for the execution block hash of the most recently included payload.
69
- * Pre-gloas: returns latestExecutionPayloadHeader.blockHash (bellatrix–fulu).
70
- * Gloas+: returns the dedicated latestBlockHash state field (EIP-7732).
71
- * Throws before bellatrix.
72
- */
73
- latestBlockHash: Bytes32;
74
- /**
75
- * The execution block number of the most recently included payload.
76
- * Named payloadBlockNumber (not latestBlockNumber) to mirror ExecutionPayloadHeader.blockNumber pre-gloas.
77
- * Only available from bellatrix through fulu — not tracked on BeaconState in gloas+ (EIP-7732).
78
- * Throws before bellatrix and from gloas onwards.
79
- */
80
- payloadBlockNumber: number;
81
-
82
- // capella
83
- historicalSummaries: capella.HistoricalSummaries;
84
-
85
- // electra
86
- pendingDeposits: electra.PendingDeposits;
87
- pendingDepositsCount: number;
88
- pendingPartialWithdrawals: electra.PendingPartialWithdrawals;
89
- pendingPartialWithdrawalsCount: number;
90
- pendingConsolidations: electra.PendingConsolidations;
91
- pendingConsolidationsCount: number;
92
-
93
- // fulu
94
- proposerLookahead: fulu.ProposerLookahead;
95
-
96
- // gloas
97
- executionPayloadAvailability: BitArray;
98
- latestExecutionPayloadBid: ExecutionPayloadBid;
99
- getBuilder(index: BuilderIndex): gloas.Builder;
100
- canBuilderCoverBid(builderIndex: BuilderIndex, bidAmount: number): boolean;
101
- getIndexInPayloadTimelinessCommittee(validatorIndex: ValidatorIndex, slot: Slot): number;
102
-
103
76
  // Shuffling and committees
104
77
  getShufflingAtEpoch(epoch: Epoch): EpochShuffling;
105
78
  // Decision roots
@@ -117,15 +90,6 @@ export interface IBeaconStateView {
117
90
  nextProposers: ValidatorIndex[];
118
91
  getBeaconProposer(slot: Slot): ValidatorIndex;
119
92
 
120
- // Sync committees
121
- currentSyncCommittee: altair.SyncCommittee;
122
- nextSyncCommittee: altair.SyncCommittee;
123
- currentSyncCommitteeIndexed: SyncCommitteeCache;
124
- syncProposerReward: number;
125
- getIndexedSyncCommitteeAtEpoch(epoch: Epoch): SyncCommitteeCache;
126
- /** Get indexed sync committee with slot+1 offset for duty lookups */
127
- getIndexedSyncCommittee(slot: Slot): SyncCommitteeCache;
128
-
129
93
  // Validators and balances
130
94
  effectiveBalanceIncrements: EffectiveBalanceIncrements;
131
95
  getEffectiveBalanceIncrementsZeroInactive(): EffectiveBalanceIncrements;
@@ -140,29 +104,10 @@ export interface IBeaconStateView {
140
104
  getAllValidators(): phase0.Validator[];
141
105
  getAllBalances(): number[];
142
106
 
143
- // Merge
144
- isExecutionStateType: boolean;
145
- isMergeTransitionComplete: boolean;
146
- // TODO this should go away (or rather only need block)
147
- isExecutionEnabled(block: BeaconBlock | BlindedBeaconBlock): boolean;
148
-
149
- // Block production
150
- getExpectedWithdrawals(): {
151
- expectedWithdrawals: capella.Withdrawal[];
152
- processedBuilderWithdrawalsCount: number;
153
- processedPartialWithdrawalsCount: number;
154
- processedBuildersSweepCount: number;
155
- processedValidatorSweepCount: number;
156
- };
157
-
158
107
  // API
159
108
  proposerRewards: RewardCache;
160
109
  computeBlockRewards(block: BeaconBlock, proposerRewards?: RewardCache): Promise<rewards.BlockRewards>;
161
110
  computeAttestationsRewards(validatorIds?: (ValidatorIndex | string)[]): Promise<rewards.AttestationsRewards>;
162
- computeSyncCommitteeRewards(
163
- block: BeaconBlock,
164
- validatorIds: (ValidatorIndex | string)[]
165
- ): Promise<rewards.SyncCommitteeRewards>;
166
111
  getLatestWeakSubjectivityCheckpointEpoch(): Epoch;
167
112
 
168
113
  // Validation
@@ -174,7 +119,6 @@ export interface IBeaconStateView {
174
119
 
175
120
  // Proofs
176
121
  getFinalizedRootProof(): Uint8Array[];
177
- getSyncCommitteesWitness(): SyncCommitteeWitness;
178
122
  getSingleProof(gindex: bigint): Uint8Array[];
179
123
  createMultiProof(descriptor: Uint8Array): CompactMultiProof;
180
124
 
@@ -193,7 +137,13 @@ export interface IBeaconStateView {
193
137
  isStateValidatorsNodesPopulated(): boolean;
194
138
 
195
139
  // Serialization
196
- loadOtherState(stateBytes: Uint8Array, seedValidatorsBytes?: Uint8Array): IBeaconStateView;
140
+ /** Set `preloadValidatorsAndBalances` only when the whole state will be consumed
141
+ * immediately (e.g. CP reload before block replay). */
142
+ loadOtherState(
143
+ stateBytes: Uint8Array,
144
+ seedValidatorsBytes?: Uint8Array,
145
+ opts?: {preloadValidatorsAndBalances?: boolean}
146
+ ): IBeaconStateView;
197
147
  toValue(): BeaconState;
198
148
  serialize(): Uint8Array;
199
149
  serializedSize(): number;
@@ -215,8 +165,140 @@ export interface IBeaconStateView {
215
165
  epochTransitionCacheOpts?: EpochTransitionCacheOpts & {dontTransferCache?: boolean},
216
166
  modules?: StateTransitionModules
217
167
  ): IBeaconStateView;
218
- processExecutionPayloadEnvelope(
219
- signedEnvelope: gloas.SignedExecutionPayloadEnvelope,
220
- opts?: ProcessExecutionPayloadEnvelopeOpts
221
- ): IBeaconStateView;
168
+ }
169
+
170
+ /** Altair+ state fields — use isStatePostAltair() guard */
171
+ export interface IBeaconStateViewAltair extends IBeaconStateView {
172
+ forkName: ForkPostAltair;
173
+ previousEpochParticipation: Uint8Array;
174
+ currentEpochParticipation: Uint8Array;
175
+ getPreviousEpochParticipation(validatorIndex: ValidatorIndex): number;
176
+ getCurrentEpochParticipation(validatorIndex: ValidatorIndex): number;
177
+ currentSyncCommittee: altair.SyncCommittee;
178
+ nextSyncCommittee: altair.SyncCommittee;
179
+ currentSyncCommitteeIndexed: SyncCommitteeCache;
180
+ syncProposerReward: number;
181
+ getIndexedSyncCommitteeAtEpoch(epoch: Epoch): SyncCommitteeCache;
182
+ /** Get indexed sync committee with slot+1 offset for duty lookups */
183
+ getIndexedSyncCommittee(slot: Slot): SyncCommitteeCache;
184
+ computeSyncCommitteeRewards(
185
+ block: BeaconBlock,
186
+ validatorIds: (ValidatorIndex | string)[]
187
+ ): Promise<rewards.SyncCommitteeRewards>;
188
+ getSyncCommitteesWitness(): SyncCommitteeWitness;
189
+ }
190
+
191
+ /** Bellatrix+ state fields — use isStatePostBellatrix() guard */
192
+ export interface IBeaconStateViewBellatrix extends IBeaconStateViewAltair {
193
+ forkName: ForkPostBellatrix;
194
+ latestExecutionPayloadHeader: ExecutionPayloadHeader;
195
+ /**
196
+ * The execution block number of the most recently included payload.
197
+ * Named payloadBlockNumber (not latestBlockNumber) to mirror ExecutionPayloadHeader.blockNumber pre-gloas.
198
+ * Only available from bellatrix through fulu — not tracked on BeaconState in gloas+ (EIP-7732).
199
+ * Throws before bellatrix and from gloas onwards.
200
+ */
201
+ payloadBlockNumber: number;
202
+ isExecutionStateType: boolean;
203
+ isMergeTransitionComplete: boolean;
204
+ isExecutionEnabled(block: BeaconBlock | BlindedBeaconBlock): boolean;
205
+ }
206
+
207
+ /** Capella+ state fields — use isStatePostCapella() guard */
208
+ export interface IBeaconStateViewCapella extends IBeaconStateViewBellatrix {
209
+ forkName: ForkPostCapella;
210
+ historicalSummaries: capella.HistoricalSummaries;
211
+ getExpectedWithdrawals(): {
212
+ expectedWithdrawals: capella.Withdrawal[];
213
+ processedBuilderWithdrawalsCount: number;
214
+ processedPartialWithdrawalsCount: number;
215
+ processedBuildersSweepCount: number;
216
+ processedValidatorSweepCount: number;
217
+ };
218
+ }
219
+
220
+ /** Deneb+ state — no new state-view fields; placeholder for fork completeness and isStatePostDeneb() narrowing */
221
+ export interface IBeaconStateViewDeneb extends IBeaconStateViewCapella {
222
+ forkName: ForkPostDeneb;
223
+ }
224
+
225
+ /** Electra+ state fields — use isStatePostElectra() guard */
226
+ export interface IBeaconStateViewElectra extends IBeaconStateViewDeneb {
227
+ forkName: ForkPostElectra;
228
+ pendingDeposits: electra.PendingDeposits;
229
+ pendingDepositsCount: number;
230
+ pendingPartialWithdrawals: electra.PendingPartialWithdrawals;
231
+ pendingPartialWithdrawalsCount: number;
232
+ pendingConsolidations: electra.PendingConsolidations;
233
+ pendingConsolidationsCount: number;
234
+ }
235
+
236
+ /** Fulu+ state fields — use isStatePostFulu() guard */
237
+ export interface IBeaconStateViewFulu extends IBeaconStateViewElectra {
238
+ forkName: ForkPostFulu;
239
+ proposerLookahead: fulu.ProposerLookahead;
240
+ }
241
+
242
+ /** Gloas+ state fields — use isStatePostGloas() guard */
243
+ export interface IBeaconStateViewGloas extends IBeaconStateViewFulu {
244
+ forkName: ForkPostGloas;
245
+ /** Removed from BeaconState in gloas. Use `latestBlockHash` instead. */
246
+ latestExecutionPayloadHeader: never;
247
+ /** Removed from BeaconState in gloas. */
248
+ payloadBlockNumber: never;
249
+ latestBlockHash: Bytes32;
250
+ executionPayloadAvailability: BitArray;
251
+ latestExecutionPayloadBid: ExecutionPayloadBid;
252
+ payloadExpectedWithdrawals: capella.Withdrawal[];
253
+ getBuilder(index: BuilderIndex): gloas.Builder;
254
+ canBuilderCoverBid(builderIndex: BuilderIndex, bidAmount: number): boolean;
255
+ getIndexInPayloadTimelinessCommittee(validatorIndex: ValidatorIndex, slot: Slot): number;
256
+ /**
257
+ * Compute expected withdrawals as if the parent was FULL.
258
+ * Clones the state, applies parent payload effects, then computes withdrawals.
259
+ * Used by prepare_execution_payload when building on FULL parent.
260
+ */
261
+ getExpectedWithdrawalsForFullParent(envelope: gloas.SignedExecutionPayloadEnvelope): capella.Withdrawal[];
262
+ }
263
+
264
+ /**
265
+ * Type constraint for the concrete BeaconStateView class.
266
+ * Requires all fields from the latest fork interface (IBeaconStateViewGloas) but keeps
267
+ * forkName as ForkName since the class wraps any fork's state.
268
+ * Sub-interfaces retain their narrowed forkName discriminants for caller-side type guards.
269
+ */
270
+ export type IBeaconStateViewLatestFork = Omit<
271
+ IBeaconStateViewGloas,
272
+ "forkName" | "latestExecutionPayloadHeader" | "payloadBlockNumber"
273
+ > & {
274
+ forkName: ForkName;
275
+ latestExecutionPayloadHeader: ExecutionPayloadHeader;
276
+ payloadBlockNumber: number;
277
+ };
278
+ export function isStatePostAltair(state: IBeaconStateView): state is IBeaconStateViewAltair {
279
+ return isForkPostAltair(state.forkName);
280
+ }
281
+
282
+ export function isStatePostBellatrix(state: IBeaconStateView): state is IBeaconStateViewBellatrix {
283
+ return isForkPostBellatrix(state.forkName);
284
+ }
285
+
286
+ export function isStatePostCapella(state: IBeaconStateView): state is IBeaconStateViewCapella {
287
+ return isForkPostCapella(state.forkName);
288
+ }
289
+
290
+ export function isStatePostDeneb(state: IBeaconStateView): state is IBeaconStateViewDeneb {
291
+ return isForkPostDeneb(state.forkName);
292
+ }
293
+
294
+ export function isStatePostElectra(state: IBeaconStateView): state is IBeaconStateViewElectra {
295
+ return isForkPostElectra(state.forkName);
296
+ }
297
+
298
+ export function isStatePostFulu(state: IBeaconStateView): state is IBeaconStateViewFulu {
299
+ return isForkPostFulu(state.forkName);
300
+ }
301
+
302
+ export function isStatePostGloas(state: IBeaconStateView): state is IBeaconStateViewGloas {
303
+ return isForkPostGloas(state.forkName);
222
304
  }