@lodestar/state-transition 1.42.0 → 1.43.0-dev.0bc48d3b54

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 (78) hide show
  1. package/lib/block/processDepositRequest.d.ts +11 -2
  2. package/lib/block/processDepositRequest.d.ts.map +1 -1
  3. package/lib/block/processDepositRequest.js +34 -4
  4. package/lib/block/processDepositRequest.js.map +1 -1
  5. package/lib/block/processExecutionPayloadEnvelope.d.ts.map +1 -1
  6. package/lib/block/processExecutionPayloadEnvelope.js +7 -3
  7. package/lib/block/processExecutionPayloadEnvelope.js.map +1 -1
  8. package/lib/cache/epochCache.d.ts +3 -1
  9. package/lib/cache/epochCache.d.ts.map +1 -1
  10. package/lib/cache/epochCache.js +31 -13
  11. package/lib/cache/epochCache.js.map +1 -1
  12. package/lib/cache/epochTransitionCache.d.ts +5 -0
  13. package/lib/cache/epochTransitionCache.d.ts.map +1 -1
  14. package/lib/cache/epochTransitionCache.js +1 -0
  15. package/lib/cache/epochTransitionCache.js.map +1 -1
  16. package/lib/epoch/index.d.ts +3 -1
  17. package/lib/epoch/index.d.ts.map +1 -1
  18. package/lib/epoch/index.js +8 -1
  19. package/lib/epoch/index.js.map +1 -1
  20. package/lib/epoch/processPtcWindow.d.ts +11 -0
  21. package/lib/epoch/processPtcWindow.d.ts.map +1 -0
  22. package/lib/epoch/processPtcWindow.js +28 -0
  23. package/lib/epoch/processPtcWindow.js.map +1 -0
  24. package/lib/index.d.ts +4 -2
  25. package/lib/index.d.ts.map +1 -1
  26. package/lib/index.js +4 -1
  27. package/lib/index.js.map +1 -1
  28. package/lib/signatureSets/executionPayloadEnvelope.d.ts.map +1 -1
  29. package/lib/signatureSets/executionPayloadEnvelope.js +4 -0
  30. package/lib/signatureSets/executionPayloadEnvelope.js.map +1 -1
  31. package/lib/signatureSets/voluntaryExits.d.ts +2 -2
  32. package/lib/signatureSets/voluntaryExits.d.ts.map +1 -1
  33. package/lib/signatureSets/voluntaryExits.js +4 -0
  34. package/lib/signatureSets/voluntaryExits.js.map +1 -1
  35. package/lib/slot/upgradeStateToGloas.d.ts.map +1 -1
  36. package/lib/slot/upgradeStateToGloas.js +2 -1
  37. package/lib/slot/upgradeStateToGloas.js.map +1 -1
  38. package/lib/stateTransition.d.ts +1 -2
  39. package/lib/stateTransition.d.ts.map +1 -1
  40. package/lib/stateTransition.js +1 -2
  41. package/lib/stateTransition.js.map +1 -1
  42. package/lib/stateView/beaconStateView.d.ts +10 -10
  43. package/lib/stateView/beaconStateView.d.ts.map +1 -1
  44. package/lib/stateView/beaconStateView.js +37 -34
  45. package/lib/stateView/beaconStateView.js.map +1 -1
  46. package/lib/stateView/interface.d.ts +103 -52
  47. package/lib/stateView/interface.d.ts.map +1 -1
  48. package/lib/stateView/interface.js +22 -1
  49. package/lib/stateView/interface.js.map +1 -1
  50. package/lib/util/attestation.d.ts +12 -1
  51. package/lib/util/attestation.d.ts.map +1 -1
  52. package/lib/util/attestation.js +23 -8
  53. package/lib/util/attestation.js.map +1 -1
  54. package/lib/util/execution.d.ts +4 -2
  55. package/lib/util/execution.d.ts.map +1 -1
  56. package/lib/util/execution.js +7 -0
  57. package/lib/util/execution.js.map +1 -1
  58. package/lib/util/gloas.d.ts +7 -1
  59. package/lib/util/gloas.d.ts.map +1 -1
  60. package/lib/util/gloas.js +26 -1
  61. package/lib/util/gloas.js.map +1 -1
  62. package/package.json +8 -8
  63. package/src/block/processDepositRequest.ts +50 -5
  64. package/src/block/processExecutionPayloadEnvelope.ts +8 -3
  65. package/src/cache/epochCache.ts +32 -30
  66. package/src/cache/epochTransitionCache.ts +7 -0
  67. package/src/epoch/index.ts +9 -0
  68. package/src/epoch/processPtcWindow.ts +38 -0
  69. package/src/index.ts +20 -2
  70. package/src/signatureSets/executionPayloadEnvelope.ts +5 -1
  71. package/src/signatureSets/voluntaryExits.ts +5 -2
  72. package/src/slot/upgradeStateToGloas.ts +2 -1
  73. package/src/stateTransition.ts +1 -2
  74. package/src/stateView/beaconStateView.ts +57 -41
  75. package/src/stateView/interface.ts +155 -74
  76. package/src/util/attestation.ts +37 -8
  77. package/src/util/execution.ts +11 -1
  78. package/src/util/gloas.ts +48 -1
@@ -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
  /**
@@ -61,6 +61,7 @@ export function upgradeStateToGloas(stateFulu: CachedBeaconStateFulu): CachedBea
61
61
  stateGloasView.pendingPartialWithdrawals = stateGloasCloned.pendingPartialWithdrawals;
62
62
  stateGloasView.pendingConsolidations = stateGloasCloned.pendingConsolidations;
63
63
  stateGloasView.proposerLookahead = stateGloasCloned.proposerLookahead;
64
+ stateGloasView.ptcWindow = ssz.gloas.PtcWindow.toViewDU(initializePtcWindow(stateFulu));
64
65
 
65
66
  for (let i = 0; i < SLOTS_PER_HISTORICAL_ROOT; i++) {
66
67
  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, isForkPostGloas} from "@lodestar/params";
5
5
  import {
6
6
  BeaconBlock,
7
7
  BeaconState,
@@ -59,14 +59,19 @@ import {getBlockRootAtSlot} from "../util/blockRoot.js";
59
59
  import {computeAnchorCheckpoint} from "../util/computeAnchorCheckpoint.js";
60
60
  import {computeEpochAtSlot, computeStartSlotAtEpoch} from "../util/epoch.js";
61
61
  import {EpochShuffling} from "../util/epochShuffling.js";
62
- import {isExecutionEnabled, isExecutionStateType, isMergeTransitionComplete} from "../util/execution.js";
62
+ import {
63
+ isExecutionEnabled,
64
+ isExecutionStateType,
65
+ isGloasStateType,
66
+ isMergeTransitionComplete,
67
+ } from "../util/execution.js";
63
68
  import {canBuilderCoverBid} from "../util/gloas.js";
64
69
  import {loadState} from "../util/loadState/loadState.js";
65
70
  import {getRandaoMix} from "../util/seed.js";
66
71
  import {getLatestWeakSubjectivityCheckpointEpoch} from "../util/weakSubjectivity.js";
67
- import {IBeaconStateView} from "./interface.js";
72
+ import {IBeaconStateView, IBeaconStateViewLatestFork} from "./interface.js";
68
73
 
69
- export class BeaconStateView implements IBeaconStateView {
74
+ export class BeaconStateView implements IBeaconStateViewLatestFork {
70
75
  private readonly config: BeaconConfig;
71
76
  // Cached values extracted from the tree
72
77
  // phase0
@@ -79,8 +84,6 @@ export class BeaconStateView implements IBeaconStateView {
79
84
  private _currentEpochParticipation: Uint8Array | null = null;
80
85
  // bellatrix
81
86
  private _latestExecutionPayloadHeader: ExecutionPayloadHeader | null = null;
82
- // Caches the cross-fork latestBlockHash value
83
- private _latestBlockHash: Bytes32 | null = null;
84
87
  // capella
85
88
  private _historicalSummaries: capella.HistoricalSummaries | null = null;
86
89
  // electra
@@ -92,6 +95,7 @@ export class BeaconStateView implements IBeaconStateView {
92
95
  // gloas
93
96
  private _executionPayloadAvailability: BitArray | null = null;
94
97
  private _latestExecutionPayloadBid: ExecutionPayloadBid | null = null;
98
+ private _payloadExpectedWithdrawals: capella.Withdrawal[] | null = null;
95
99
 
96
100
  constructor(readonly cachedState: CachedBeaconStateAllForks) {
97
101
  this.config = cachedState.config;
@@ -99,6 +103,10 @@ export class BeaconStateView implements IBeaconStateView {
99
103
 
100
104
  // phase0
101
105
 
106
+ get forkName(): ForkName {
107
+ return this.config.getForkName(this.cachedState.slot);
108
+ }
109
+
102
110
  get slot(): number {
103
111
  return this.cachedState.slot;
104
112
  }
@@ -208,9 +216,13 @@ export class BeaconStateView implements IBeaconStateView {
208
216
  // bellatrix
209
217
 
210
218
  get latestExecutionPayloadHeader(): ExecutionPayloadHeader {
211
- if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.bellatrix) {
219
+ const forkSeq = this.config.getForkSeq(this.cachedState.slot);
220
+ if (forkSeq < ForkSeq.bellatrix) {
212
221
  throw new Error("latestExecutionPayloadHeader is not available before Bellatrix");
213
222
  }
223
+ if (forkSeq >= ForkSeq.gloas) {
224
+ throw new Error("latestExecutionPayloadHeader is not available after Gloas");
225
+ }
214
226
 
215
227
  if (this._latestExecutionPayloadHeader === null) {
216
228
  this._latestExecutionPayloadHeader = (
@@ -221,30 +233,6 @@ export class BeaconStateView implements IBeaconStateView {
221
233
  return this._latestExecutionPayloadHeader;
222
234
  }
223
235
 
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
236
  /**
249
237
  * The execution block number of the most recently included payload.
250
238
  * Named payloadBlockNumber (not latestBlockNumber) to mirror ExecutionPayloadHeader.blockNumber pre-gloas.
@@ -355,9 +343,16 @@ export class BeaconStateView implements IBeaconStateView {
355
343
 
356
344
  // gloas
357
345
 
346
+ get latestBlockHash(): Bytes32 {
347
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
348
+ throw new Error("latestBlockHash is not available before Gloas");
349
+ }
350
+ return (this.cachedState as CachedBeaconStateGloas).latestBlockHash;
351
+ }
352
+
358
353
  get executionPayloadAvailability(): BitArray {
359
354
  if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
360
- throw new Error("executionPayloadAvailability is not available before GLOAS");
355
+ throw new Error("executionPayloadAvailability is not available before Gloas");
361
356
  }
362
357
 
363
358
  if (this._executionPayloadAvailability === null) {
@@ -371,7 +366,7 @@ export class BeaconStateView implements IBeaconStateView {
371
366
 
372
367
  get latestExecutionPayloadBid(): ExecutionPayloadBid {
373
368
  if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
374
- throw new Error("latestExecutionPayloadBid is not available before GLOAS");
369
+ throw new Error("latestExecutionPayloadBid is not available before Gloas");
375
370
  }
376
371
 
377
372
  if (this._latestExecutionPayloadBid === null) {
@@ -382,9 +377,22 @@ export class BeaconStateView implements IBeaconStateView {
382
377
  return this._latestExecutionPayloadBid;
383
378
  }
384
379
 
380
+ get payloadExpectedWithdrawals(): capella.Withdrawal[] {
381
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
382
+ throw new Error("payloadExpectedWithdrawals is not available before Gloas");
383
+ }
384
+
385
+ if (this._payloadExpectedWithdrawals === null) {
386
+ this._payloadExpectedWithdrawals = (
387
+ this.cachedState as CachedBeaconStateGloas
388
+ ).payloadExpectedWithdrawals.toValue();
389
+ }
390
+ return this._payloadExpectedWithdrawals;
391
+ }
392
+
385
393
  getBuilder(index: BuilderIndex): gloas.Builder {
386
394
  if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
387
- throw new Error("Builders are not supported before GLOAS");
395
+ throw new Error("Builders are not supported before Gloas");
388
396
  }
389
397
 
390
398
  return (this.cachedState as CachedBeaconStateGloas).builders.getReadonly(index);
@@ -392,7 +400,7 @@ export class BeaconStateView implements IBeaconStateView {
392
400
 
393
401
  canBuilderCoverBid(builderIndex: BuilderIndex, bidAmount: number): boolean {
394
402
  if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
395
- throw new Error("Builders are not supported before GLOAS");
403
+ throw new Error("Builders are not supported before Gloas");
396
404
  }
397
405
 
398
406
  return canBuilderCoverBid(this.cachedState as CachedBeaconStateGloas, builderIndex, bidAmount);
@@ -404,7 +412,7 @@ export class BeaconStateView implements IBeaconStateView {
404
412
  */
405
413
  getIndexInPayloadTimelinessCommittee(validatorIndex: ValidatorIndex, slot: Slot): number {
406
414
  if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
407
- throw new Error("PTC committees are not supported before GLOAS");
415
+ throw new Error("PTC committees are not supported before Gloas");
408
416
  }
409
417
 
410
418
  const ptcCommittee = (this.cachedState as CachedBeaconStateGloas).epochCtx.getPayloadTimelinessCommittee(slot);
@@ -571,7 +579,10 @@ export class BeaconStateView implements IBeaconStateView {
571
579
  }
572
580
 
573
581
  get isMergeTransitionComplete(): boolean {
574
- return isExecutionStateType(this.cachedState) && isMergeTransitionComplete(this.cachedState);
582
+ return (
583
+ (isExecutionStateType(this.cachedState) || isGloasStateType(this.cachedState)) &&
584
+ isMergeTransitionComplete(this.cachedState)
585
+ );
575
586
  }
576
587
 
577
588
  // Block production
@@ -693,7 +704,11 @@ export class BeaconStateView implements IBeaconStateView {
693
704
 
694
705
  // Serialization
695
706
 
696
- loadOtherState(stateBytes: Uint8Array, seedValidatorsBytes?: Uint8Array): IBeaconStateView {
707
+ loadOtherState(
708
+ stateBytes: Uint8Array,
709
+ seedValidatorsBytes?: Uint8Array,
710
+ opts?: {preloadValidatorsAndBalances?: boolean}
711
+ ): IBeaconStateView {
697
712
  const {state} = loadState(this.config, this.cachedState, stateBytes, seedValidatorsBytes);
698
713
 
699
714
  const cachedState = createCachedBeaconState(
@@ -708,9 +723,10 @@ export class BeaconStateView implements IBeaconStateView {
708
723
  }
709
724
  );
710
725
 
711
- // load all cache in order for consumers (usually regen.getState()) to process blocks faster
712
- cachedState.validators.getAllReadonlyValues();
713
- cachedState.balances.getAll();
726
+ if (opts?.preloadValidatorsAndBalances) {
727
+ cachedState.validators.getAllReadonlyValues();
728
+ cachedState.balances.getAll();
729
+ }
714
730
 
715
731
  return new BeaconStateView(cachedState);
716
732
  }
@@ -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,
@@ -41,6 +58,7 @@ export interface IBeaconStateView {
41
58
  // State access
42
59
 
43
60
  // phase0
61
+ forkName: ForkName;
44
62
  slot: Slot;
45
63
  fork: Fork;
46
64
  epoch: Epoch;
@@ -56,50 +74,6 @@ export interface IBeaconStateView {
56
74
  getStateRootAtSlot(slot: Slot): Root;
57
75
  getRandaoMix(epoch: Epoch): Bytes32;
58
76
 
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
77
  // Shuffling and committees
104
78
  getShufflingAtEpoch(epoch: Epoch): EpochShuffling;
105
79
  // Decision roots
@@ -117,15 +91,6 @@ export interface IBeaconStateView {
117
91
  nextProposers: ValidatorIndex[];
118
92
  getBeaconProposer(slot: Slot): ValidatorIndex;
119
93
 
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
94
  // Validators and balances
130
95
  effectiveBalanceIncrements: EffectiveBalanceIncrements;
131
96
  getEffectiveBalanceIncrementsZeroInactive(): EffectiveBalanceIncrements;
@@ -140,29 +105,10 @@ export interface IBeaconStateView {
140
105
  getAllValidators(): phase0.Validator[];
141
106
  getAllBalances(): number[];
142
107
 
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
108
  // API
159
109
  proposerRewards: RewardCache;
160
110
  computeBlockRewards(block: BeaconBlock, proposerRewards?: RewardCache): Promise<rewards.BlockRewards>;
161
111
  computeAttestationsRewards(validatorIds?: (ValidatorIndex | string)[]): Promise<rewards.AttestationsRewards>;
162
- computeSyncCommitteeRewards(
163
- block: BeaconBlock,
164
- validatorIds: (ValidatorIndex | string)[]
165
- ): Promise<rewards.SyncCommitteeRewards>;
166
112
  getLatestWeakSubjectivityCheckpointEpoch(): Epoch;
167
113
 
168
114
  // Validation
@@ -174,7 +120,6 @@ export interface IBeaconStateView {
174
120
 
175
121
  // Proofs
176
122
  getFinalizedRootProof(): Uint8Array[];
177
- getSyncCommitteesWitness(): SyncCommitteeWitness;
178
123
  getSingleProof(gindex: bigint): Uint8Array[];
179
124
  createMultiProof(descriptor: Uint8Array): CompactMultiProof;
180
125
 
@@ -193,7 +138,13 @@ export interface IBeaconStateView {
193
138
  isStateValidatorsNodesPopulated(): boolean;
194
139
 
195
140
  // Serialization
196
- loadOtherState(stateBytes: Uint8Array, seedValidatorsBytes?: Uint8Array): IBeaconStateView;
141
+ /** Set `preloadValidatorsAndBalances` only when the whole state will be consumed
142
+ * immediately (e.g. CP reload before block replay). */
143
+ loadOtherState(
144
+ stateBytes: Uint8Array,
145
+ seedValidatorsBytes?: Uint8Array,
146
+ opts?: {preloadValidatorsAndBalances?: boolean}
147
+ ): IBeaconStateView;
197
148
  toValue(): BeaconState;
198
149
  serialize(): Uint8Array;
199
150
  serializedSize(): number;
@@ -215,8 +166,138 @@ export interface IBeaconStateView {
215
166
  epochTransitionCacheOpts?: EpochTransitionCacheOpts & {dontTransferCache?: boolean},
216
167
  modules?: StateTransitionModules
217
168
  ): IBeaconStateView;
169
+ }
170
+
171
+ /** Altair+ state fields — use isStatePostAltair() guard */
172
+ export interface IBeaconStateViewAltair extends IBeaconStateView {
173
+ forkName: ForkPostAltair;
174
+ previousEpochParticipation: Uint8Array;
175
+ currentEpochParticipation: Uint8Array;
176
+ getPreviousEpochParticipation(validatorIndex: ValidatorIndex): number;
177
+ getCurrentEpochParticipation(validatorIndex: ValidatorIndex): number;
178
+ currentSyncCommittee: altair.SyncCommittee;
179
+ nextSyncCommittee: altair.SyncCommittee;
180
+ currentSyncCommitteeIndexed: SyncCommitteeCache;
181
+ syncProposerReward: number;
182
+ getIndexedSyncCommitteeAtEpoch(epoch: Epoch): SyncCommitteeCache;
183
+ /** Get indexed sync committee with slot+1 offset for duty lookups */
184
+ getIndexedSyncCommittee(slot: Slot): SyncCommitteeCache;
185
+ computeSyncCommitteeRewards(
186
+ block: BeaconBlock,
187
+ validatorIds: (ValidatorIndex | string)[]
188
+ ): Promise<rewards.SyncCommitteeRewards>;
189
+ getSyncCommitteesWitness(): SyncCommitteeWitness;
190
+ }
191
+
192
+ /** Bellatrix+ state fields — use isStatePostBellatrix() guard */
193
+ export interface IBeaconStateViewBellatrix extends IBeaconStateViewAltair {
194
+ forkName: ForkPostBellatrix;
195
+ latestExecutionPayloadHeader: ExecutionPayloadHeader;
196
+ /**
197
+ * The execution block number of the most recently included payload.
198
+ * Named payloadBlockNumber (not latestBlockNumber) to mirror ExecutionPayloadHeader.blockNumber pre-gloas.
199
+ * Only available from bellatrix through fulu — not tracked on BeaconState in gloas+ (EIP-7732).
200
+ * Throws before bellatrix and from gloas onwards.
201
+ */
202
+ payloadBlockNumber: number;
203
+ isExecutionStateType: boolean;
204
+ isMergeTransitionComplete: boolean;
205
+ isExecutionEnabled(block: BeaconBlock | BlindedBeaconBlock): boolean;
206
+ }
207
+
208
+ /** Capella+ state fields — use isStatePostCapella() guard */
209
+ export interface IBeaconStateViewCapella extends IBeaconStateViewBellatrix {
210
+ forkName: ForkPostCapella;
211
+ historicalSummaries: capella.HistoricalSummaries;
212
+ getExpectedWithdrawals(): {
213
+ expectedWithdrawals: capella.Withdrawal[];
214
+ processedBuilderWithdrawalsCount: number;
215
+ processedPartialWithdrawalsCount: number;
216
+ processedBuildersSweepCount: number;
217
+ processedValidatorSweepCount: number;
218
+ };
219
+ }
220
+
221
+ /** Deneb+ state — no new state-view fields; placeholder for fork completeness and isStatePostDeneb() narrowing */
222
+ export interface IBeaconStateViewDeneb extends IBeaconStateViewCapella {
223
+ forkName: ForkPostDeneb;
224
+ }
225
+
226
+ /** Electra+ state fields — use isStatePostElectra() guard */
227
+ export interface IBeaconStateViewElectra extends IBeaconStateViewDeneb {
228
+ forkName: ForkPostElectra;
229
+ pendingDeposits: electra.PendingDeposits;
230
+ pendingDepositsCount: number;
231
+ pendingPartialWithdrawals: electra.PendingPartialWithdrawals;
232
+ pendingPartialWithdrawalsCount: number;
233
+ pendingConsolidations: electra.PendingConsolidations;
234
+ pendingConsolidationsCount: number;
235
+ }
236
+
237
+ /** Fulu+ state fields — use isStatePostFulu() guard */
238
+ export interface IBeaconStateViewFulu extends IBeaconStateViewElectra {
239
+ forkName: ForkPostFulu;
240
+ proposerLookahead: fulu.ProposerLookahead;
241
+ }
242
+
243
+ /** Gloas+ state fields — use isStatePostGloas() guard */
244
+ export interface IBeaconStateViewGloas extends IBeaconStateViewFulu {
245
+ forkName: ForkPostGloas;
246
+ /** Removed from BeaconState in gloas. Use `latestBlockHash` instead. */
247
+ latestExecutionPayloadHeader: never;
248
+ /** Removed from BeaconState in gloas. */
249
+ payloadBlockNumber: never;
250
+ latestBlockHash: Bytes32;
251
+ executionPayloadAvailability: BitArray;
252
+ latestExecutionPayloadBid: ExecutionPayloadBid;
253
+ payloadExpectedWithdrawals: capella.Withdrawal[];
254
+ getBuilder(index: BuilderIndex): gloas.Builder;
255
+ canBuilderCoverBid(builderIndex: BuilderIndex, bidAmount: number): boolean;
256
+ getIndexInPayloadTimelinessCommittee(validatorIndex: ValidatorIndex, slot: Slot): number;
218
257
  processExecutionPayloadEnvelope(
219
258
  signedEnvelope: gloas.SignedExecutionPayloadEnvelope,
220
259
  opts?: ProcessExecutionPayloadEnvelopeOpts
221
260
  ): IBeaconStateView;
222
261
  }
262
+
263
+ /**
264
+ * Type constraint for the concrete BeaconStateView class.
265
+ * Requires all fields from the latest fork interface (IBeaconStateViewGloas) but keeps
266
+ * forkName as ForkName since the class wraps any fork's state.
267
+ * Sub-interfaces retain their narrowed forkName discriminants for caller-side type guards.
268
+ */
269
+ export type IBeaconStateViewLatestFork = Omit<
270
+ IBeaconStateViewGloas,
271
+ "forkName" | "latestExecutionPayloadHeader" | "payloadBlockNumber"
272
+ > & {
273
+ forkName: ForkName;
274
+ latestExecutionPayloadHeader: ExecutionPayloadHeader;
275
+ payloadBlockNumber: number;
276
+ };
277
+ export function isStatePostAltair(state: IBeaconStateView): state is IBeaconStateViewAltair {
278
+ return isForkPostAltair(state.forkName);
279
+ }
280
+
281
+ export function isStatePostBellatrix(state: IBeaconStateView): state is IBeaconStateViewBellatrix {
282
+ return isForkPostBellatrix(state.forkName);
283
+ }
284
+
285
+ export function isStatePostCapella(state: IBeaconStateView): state is IBeaconStateViewCapella {
286
+ return isForkPostCapella(state.forkName);
287
+ }
288
+
289
+ export function isStatePostDeneb(state: IBeaconStateView): state is IBeaconStateViewDeneb {
290
+ return isForkPostDeneb(state.forkName);
291
+ }
292
+
293
+ export function isStatePostElectra(state: IBeaconStateView): state is IBeaconStateViewElectra {
294
+ return isForkPostElectra(state.forkName);
295
+ }
296
+
297
+ export function isStatePostFulu(state: IBeaconStateView): state is IBeaconStateViewFulu {
298
+ return isForkPostFulu(state.forkName);
299
+ }
300
+
301
+ export function isStatePostGloas(state: IBeaconStateView): state is IBeaconStateViewGloas {
302
+ return isForkPostGloas(state.forkName);
303
+ }
@@ -1,5 +1,13 @@
1
- import {MIN_ATTESTATION_INCLUSION_DELAY, SLOTS_PER_EPOCH} from "@lodestar/params";
2
- import {AttesterSlashing, Slot, ValidatorIndex, phase0, ssz} from "@lodestar/types";
1
+ import {ForkSeq, MIN_ATTESTATION_INCLUSION_DELAY, SLOTS_PER_EPOCH} from "@lodestar/params";
2
+ import {
3
+ AttesterSlashing,
4
+ IndexedAttestation,
5
+ IndexedAttestationBigint,
6
+ Slot,
7
+ ValidatorIndex,
8
+ phase0,
9
+ ssz,
10
+ } from "@lodestar/types";
3
11
 
4
12
  /**
5
13
  * Check if [[data1]] and [[data2]] are slashable according to Casper FFG rules.
@@ -22,15 +30,36 @@ export function isValidAttestationSlot(attestationSlot: Slot, currentSlot: Slot)
22
30
  );
23
31
  }
24
32
 
25
- export function getAttesterSlashableIndices(attesterSlashing: AttesterSlashing): ValidatorIndex[] {
33
+ /**
34
+ * Compute the intersection of two sorted validator index lists.
35
+ * Both inputs must be sorted in ascending order (per spec).
36
+ */
37
+ export function getIntersectingIndices(indices1: ValidatorIndex[], indices2: ValidatorIndex[]): ValidatorIndex[] {
26
38
  const indices: ValidatorIndex[] = [];
27
- const attSet1 = new Set(attesterSlashing.attestation1.attestingIndices);
28
- const attArr2 = attesterSlashing.attestation2.attestingIndices;
29
- for (let i = 0, len = attArr2.length; i < len; i++) {
30
- const index = attArr2[i];
31
- if (attSet1.has(index)) {
39
+ const alreadyPresent = new Set(indices1);
40
+ for (let i = 0, len = indices2.length; i < len; i++) {
41
+ const index = indices2[i];
42
+ if (alreadyPresent.has(index)) {
32
43
  indices.push(index);
33
44
  }
34
45
  }
35
46
  return indices;
36
47
  }
48
+
49
+ export function getAttesterSlashableIndices(attesterSlashing: AttesterSlashing): ValidatorIndex[] {
50
+ return getIntersectingIndices(
51
+ attesterSlashing.attestation1.attestingIndices,
52
+ attesterSlashing.attestation2.attestingIndices
53
+ );
54
+ }
55
+
56
+ /**
57
+ * Convert IndexedAttestation to IndexedAttestationBigint via SSZ roundtrip.
58
+ * Both types share the same binary layout — only the JS numeric representation differs.
59
+ */
60
+ export function toIndexedAttestationBigint(att: IndexedAttestation, fork: ForkSeq): IndexedAttestationBigint {
61
+ const sszType = fork >= ForkSeq.electra ? ssz.electra.IndexedAttestation : ssz.phase0.IndexedAttestation;
62
+ const sszTypeBigint =
63
+ fork >= ForkSeq.electra ? ssz.electra.IndexedAttestationBigint : ssz.phase0.IndexedAttestationBigint;
64
+ return sszTypeBigint.deserialize(sszType.serialize(att));
65
+ }
@@ -18,6 +18,7 @@ import {
18
18
  BeaconStateBellatrix,
19
19
  BeaconStateCapella,
20
20
  BeaconStateExecutions,
21
+ BeaconStateGloas,
21
22
  CachedBeaconStateAllForks,
22
23
  CachedBeaconStateExecutions,
23
24
  } from "../types.js";
@@ -46,7 +47,11 @@ export function isExecutionEnabled(state: BeaconStateExecutions, block: BeaconBl
46
47
  * Merge is complete when the state includes execution layer data:
47
48
  * state.latestExecutionPayloadHeader NOT EMPTY or state is post-capella
48
49
  */
49
- export function isMergeTransitionComplete(state: BeaconStateExecutions): boolean {
50
+ export function isMergeTransitionComplete(state: BeaconStateExecutions | BeaconStateGloas): boolean {
51
+ if (isGloasStateType(state)) {
52
+ return true;
53
+ }
54
+
50
55
  if (isCapellaStateType(state)) {
51
56
  // All networks have completed the merge transition before capella
52
57
  return true;
@@ -71,6 +76,11 @@ export function isCapellaStateType(state: BeaconStateAllForks): state is BeaconS
71
76
  );
72
77
  }
73
78
 
79
+ /** Type guard for gloas.BeaconState */
80
+ export function isGloasStateType(state: BeaconStateAllForks): state is BeaconStateGloas {
81
+ return (state as BeaconStateGloas).latestBlockHash !== undefined;
82
+ }
83
+
74
84
  /** Type guard for bellatrix.CachedBeaconState */
75
85
  export function isExecutionCachedStateType(state: CachedBeaconStateAllForks): state is CachedBeaconStateExecutions {
76
86
  return (state as CachedBeaconStateExecutions).latestExecutionPayloadHeader !== undefined;