@lodestar/state-transition 1.42.0-dev.f6213da56d → 1.42.0-dev.fb8f8a700e

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 (72) hide show
  1. package/lib/block/isValidIndexedAttestation.d.ts.map +1 -1
  2. package/lib/block/isValidIndexedAttestation.js +2 -3
  3. package/lib/block/isValidIndexedAttestation.js.map +1 -1
  4. package/lib/block/processAttestationsAltair.d.ts +2 -1
  5. package/lib/block/processAttestationsAltair.d.ts.map +1 -1
  6. package/lib/block/processAttestationsAltair.js +5 -3
  7. package/lib/block/processAttestationsAltair.js.map +1 -1
  8. package/lib/index.d.ts +4 -2
  9. package/lib/index.d.ts.map +1 -1
  10. package/lib/index.js +4 -1
  11. package/lib/index.js.map +1 -1
  12. package/lib/signatureSets/executionPayloadEnvelope.d.ts.map +1 -1
  13. package/lib/signatureSets/executionPayloadEnvelope.js +4 -0
  14. package/lib/signatureSets/executionPayloadEnvelope.js.map +1 -1
  15. package/lib/signatureSets/index.d.ts +2 -2
  16. package/lib/signatureSets/index.d.ts.map +1 -1
  17. package/lib/signatureSets/index.js +1 -2
  18. package/lib/signatureSets/index.js.map +1 -1
  19. package/lib/signatureSets/voluntaryExits.d.ts +2 -2
  20. package/lib/signatureSets/voluntaryExits.d.ts.map +1 -1
  21. package/lib/signatureSets/voluntaryExits.js +4 -0
  22. package/lib/signatureSets/voluntaryExits.js.map +1 -1
  23. package/lib/slot/upgradeStateToAltair.d.ts.map +1 -1
  24. package/lib/slot/upgradeStateToAltair.js +2 -1
  25. package/lib/slot/upgradeStateToAltair.js.map +1 -1
  26. package/lib/stateView/beaconStateView.d.ts +10 -11
  27. package/lib/stateView/beaconStateView.d.ts.map +1 -1
  28. package/lib/stateView/beaconStateView.js +11 -32
  29. package/lib/stateView/beaconStateView.js.map +1 -1
  30. package/lib/stateView/index.d.ts +1 -0
  31. package/lib/stateView/index.d.ts.map +1 -1
  32. package/lib/stateView/index.js +1 -0
  33. package/lib/stateView/index.js.map +1 -1
  34. package/lib/stateView/interface.d.ts +104 -54
  35. package/lib/stateView/interface.d.ts.map +1 -1
  36. package/lib/stateView/interface.js +22 -1
  37. package/lib/stateView/interface.js.map +1 -1
  38. package/lib/stateView/stateViewFactory.d.ts +40 -0
  39. package/lib/stateView/stateViewFactory.d.ts.map +1 -0
  40. package/lib/stateView/stateViewFactory.js +46 -0
  41. package/lib/stateView/stateViewFactory.js.map +1 -0
  42. package/lib/testUtils/cache.d.ts.map +1 -1
  43. package/lib/testUtils/cache.js +1 -1
  44. package/lib/testUtils/cache.js.map +1 -1
  45. package/lib/testUtils/util.d.ts +13 -1
  46. package/lib/testUtils/util.d.ts.map +1 -1
  47. package/lib/testUtils/util.js +119 -19
  48. package/lib/testUtils/util.js.map +1 -1
  49. package/lib/util/rootCache.d.ts +2 -2
  50. package/lib/util/rootCache.d.ts.map +1 -1
  51. package/lib/util/rootCache.js +2 -3
  52. package/lib/util/rootCache.js.map +1 -1
  53. package/lib/util/shuffling.d.ts +2 -1
  54. package/lib/util/shuffling.d.ts.map +1 -1
  55. package/lib/util/shuffling.js +2 -2
  56. package/lib/util/shuffling.js.map +1 -1
  57. package/package.json +7 -7
  58. package/src/block/isValidIndexedAttestation.ts +2 -3
  59. package/src/block/processAttestationsAltair.ts +7 -4
  60. package/src/index.ts +20 -2
  61. package/src/signatureSets/executionPayloadEnvelope.ts +5 -1
  62. package/src/signatureSets/index.ts +3 -4
  63. package/src/signatureSets/voluntaryExits.ts +5 -2
  64. package/src/slot/upgradeStateToAltair.ts +2 -1
  65. package/src/stateView/beaconStateView.ts +23 -51
  66. package/src/stateView/index.ts +1 -0
  67. package/src/stateView/interface.ts +147 -73
  68. package/src/stateView/stateViewFactory.ts +78 -0
  69. package/src/testUtils/cache.ts +1 -1
  70. package/src/testUtils/util.ts +136 -22
  71. package/src/util/rootCache.ts +4 -5
  72. package/src/util/shuffling.ts +5 -4
@@ -1,9 +1,10 @@
1
1
  import {CompactMultiProof, ProofType, Tree, createProof} from "@chainsafe/persistent-merkle-tree";
2
- import {ByteViews} from "@chainsafe/ssz";
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
+ BeaconState,
7
8
  BlindedBeaconBlock,
8
9
  BuilderIndex,
9
10
  Bytes32,
@@ -33,7 +34,6 @@ import {VoluntaryExitValidity, getVoluntaryExitValidity} from "../block/processV
33
34
  import {getExpectedWithdrawals} from "../block/processWithdrawals.js";
34
35
  import {EffectiveBalanceIncrements} from "../cache/effectiveBalanceIncrements.js";
35
36
  import {EpochTransitionCacheOpts} from "../cache/epochTransitionCache.js";
36
- import {PubkeyCache, createPubkeyCache} from "../cache/pubkeyCache.js";
37
37
  import {RewardCache} from "../cache/rewardCache.js";
38
38
  import {
39
39
  CachedBeaconStateAllForks,
@@ -47,7 +47,6 @@ import {
47
47
  isStateValidatorsNodesPopulated,
48
48
  } from "../cache/stateCache.js";
49
49
  import {SyncCommitteeCache} from "../cache/syncCommitteeCache.js";
50
- import {BeaconStateAllForks} from "../cache/types.js";
51
50
  import {computeUnrealizedCheckpoints} from "../epoch/computeUnrealizedCheckpoints.js";
52
51
  import {getFinalizedRootProof, getSyncCommitteesWitness} from "../lightClient/proofs.js";
53
52
  import {SyncCommitteeWitness} from "../lightClient/types.js";
@@ -64,11 +63,10 @@ import {isExecutionEnabled, isExecutionStateType, isMergeTransitionComplete} fro
64
63
  import {canBuilderCoverBid} from "../util/gloas.js";
65
64
  import {loadState} from "../util/loadState/loadState.js";
66
65
  import {getRandaoMix} from "../util/seed.js";
67
- import {getStateTypeFromBytes} from "../util/sszBytes.js";
68
66
  import {getLatestWeakSubjectivityCheckpointEpoch} from "../util/weakSubjectivity.js";
69
- import {IBeaconStateView} from "./interface.js";
67
+ import {IBeaconStateView, IBeaconStateViewLatestFork} from "./interface.js";
70
68
 
71
- export class BeaconStateView implements IBeaconStateView {
69
+ export class BeaconStateView implements IBeaconStateViewLatestFork {
72
70
  private readonly config: BeaconConfig;
73
71
  // Cached values extracted from the tree
74
72
  // phase0
@@ -92,7 +90,7 @@ export class BeaconStateView implements IBeaconStateView {
92
90
  // fulu
93
91
  private _proposerLookahead: fulu.ProposerLookahead | null = null;
94
92
  // gloas
95
- private _executionPayloadAvailability: boolean[] | null = null;
93
+ private _executionPayloadAvailability: BitArray | null = null;
96
94
  private _latestExecutionPayloadBid: ExecutionPayloadBid | null = null;
97
95
 
98
96
  constructor(readonly cachedState: CachedBeaconStateAllForks) {
@@ -101,6 +99,10 @@ export class BeaconStateView implements IBeaconStateView {
101
99
 
102
100
  // phase0
103
101
 
102
+ get forkName(): ForkName {
103
+ return this.config.getForkName(this.cachedState.slot);
104
+ }
105
+
104
106
  get slot(): number {
105
107
  return this.cachedState.slot;
106
108
  }
@@ -357,15 +359,15 @@ export class BeaconStateView implements IBeaconStateView {
357
359
 
358
360
  // gloas
359
361
 
360
- get executionPayloadAvailability(): boolean[] {
362
+ get executionPayloadAvailability(): BitArray {
361
363
  if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
362
364
  throw new Error("executionPayloadAvailability is not available before GLOAS");
363
365
  }
364
366
 
365
367
  if (this._executionPayloadAvailability === null) {
366
- this._executionPayloadAvailability = (this.cachedState as CachedBeaconStateGloas).executionPayloadAvailability
367
- .toValue()
368
- .toBoolArray();
368
+ this._executionPayloadAvailability = (
369
+ this.cachedState as CachedBeaconStateGloas
370
+ ).executionPayloadAvailability.toValue();
369
371
  }
370
372
 
371
373
  return this._executionPayloadAvailability;
@@ -404,7 +406,7 @@ export class BeaconStateView implements IBeaconStateView {
404
406
  * Return the index of the validator in the PTC committee for the given slot.
405
407
  * return -1 if validator is not in the PTC committee for the given slot.
406
408
  */
407
- validatorPTCCommitteeIndex(validatorIndex: ValidatorIndex, slot: Slot): number {
409
+ getIndexInPayloadTimelinessCommittee(validatorIndex: ValidatorIndex, slot: Slot): number {
408
410
  if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
409
411
  throw new Error("PTC committees are not supported before GLOAS");
410
412
  }
@@ -503,6 +505,10 @@ export class BeaconStateView implements IBeaconStateView {
503
505
  return this.cachedState.epochCtx.syncProposerReward;
504
506
  }
505
507
 
508
+ getIndexedSyncCommittee(slot: Slot): SyncCommitteeCache {
509
+ return this.cachedState.epochCtx.getIndexedSyncCommittee(slot);
510
+ }
511
+
506
512
  getIndexedSyncCommitteeAtEpoch(epoch: Epoch): SyncCommitteeCache {
507
513
  return this.cachedState.epochCtx.getIndexedSyncCommitteeAtEpoch(epoch);
508
514
  }
@@ -713,6 +719,10 @@ export class BeaconStateView implements IBeaconStateView {
713
719
  return new BeaconStateView(cachedState);
714
720
  }
715
721
 
722
+ toValue(): BeaconState {
723
+ return this.cachedState.toValue();
724
+ }
725
+
716
726
  serialize(): Uint8Array {
717
727
  return this.cachedState.serialize();
718
728
  }
@@ -778,41 +788,3 @@ export class BeaconStateView implements IBeaconStateView {
778
788
  return new BeaconStateView(postPayloadState);
779
789
  }
780
790
  }
781
-
782
- /**
783
- * Create BeaconStateView for historical state regen, no need to sync pubkey cache there.
784
- */
785
- export function createBeaconStateViewForHistoricalRegen(
786
- config: BeaconConfig,
787
- stateBytes: Uint8Array
788
- ): IBeaconStateView {
789
- const state = getStateTypeFromBytes(config, stateBytes).deserializeToViewDU(stateBytes);
790
-
791
- const pubkeyCache = createPubkeyCache();
792
- syncPubkeyCache(state, pubkeyCache);
793
- const cachedState = createCachedBeaconState(
794
- state,
795
- {
796
- config,
797
- pubkeyCache,
798
- },
799
- {
800
- skipSyncPubkeys: true,
801
- }
802
- );
803
-
804
- return new BeaconStateView(cachedState);
805
- }
806
-
807
- /**
808
- * Populate a PubkeyIndexMap with any new entries based on a BeaconState
809
- */
810
- function syncPubkeyCache(state: BeaconStateAllForks, pubkeyCache: PubkeyCache): void {
811
- // Get the validators sub tree once for all the loop
812
-
813
- const newCount = state.validators.length;
814
- for (let i = pubkeyCache.size; i < newCount; i++) {
815
- const pubkey = state.validators.getReadonly(i).pubkey;
816
- pubkeyCache.set(i, pubkey);
817
- }
818
- }
@@ -1,2 +1,3 @@
1
1
  export * from "./beaconStateView.js";
2
2
  export * from "./interface.js";
3
+ export * from "./stateViewFactory.js";
@@ -1,7 +1,25 @@
1
1
  import {CompactMultiProof} from "@chainsafe/persistent-merkle-tree";
2
- import {ByteViews} from "@chainsafe/ssz";
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,
22
+ BeaconState,
5
23
  BlindedBeaconBlock,
6
24
  BuilderIndex,
7
25
  Bytes32,
@@ -40,6 +58,7 @@ export interface IBeaconStateView {
40
58
  // State access
41
59
 
42
60
  // phase0
61
+ forkName: ForkName;
43
62
  slot: Slot;
44
63
  fork: Fork;
45
64
  epoch: Epoch;
@@ -55,50 +74,6 @@ export interface IBeaconStateView {
55
74
  getStateRootAtSlot(slot: Slot): Root;
56
75
  getRandaoMix(epoch: Epoch): Bytes32;
57
76
 
58
- // altair
59
- previousEpochParticipation: Uint8Array;
60
- currentEpochParticipation: Uint8Array;
61
- getPreviousEpochParticipation(validatorIndex: ValidatorIndex): number;
62
- getCurrentEpochParticipation(validatorIndex: ValidatorIndex): number;
63
-
64
- // bellatrix
65
- latestExecutionPayloadHeader: ExecutionPayloadHeader;
66
- /**
67
- * Cross-fork accessor for the execution block hash of the most recently included payload.
68
- * Pre-gloas: returns latestExecutionPayloadHeader.blockHash (bellatrix–fulu).
69
- * Gloas+: returns the dedicated latestBlockHash state field (EIP-7732).
70
- * Throws before bellatrix.
71
- */
72
- latestBlockHash: Bytes32;
73
- /**
74
- * The execution block number of the most recently included payload.
75
- * Named payloadBlockNumber (not latestBlockNumber) to mirror ExecutionPayloadHeader.blockNumber pre-gloas.
76
- * Only available from bellatrix through fulu — not tracked on BeaconState in gloas+ (EIP-7732).
77
- * Throws before bellatrix and from gloas onwards.
78
- */
79
- payloadBlockNumber: number;
80
-
81
- // capella
82
- historicalSummaries: capella.HistoricalSummaries;
83
-
84
- // electra
85
- pendingDeposits: electra.PendingDeposits;
86
- pendingDepositsCount: number;
87
- pendingPartialWithdrawals: electra.PendingPartialWithdrawals;
88
- pendingPartialWithdrawalsCount: number;
89
- pendingConsolidations: electra.PendingConsolidations;
90
- pendingConsolidationsCount: number;
91
-
92
- // fulu
93
- proposerLookahead: fulu.ProposerLookahead;
94
-
95
- // gloas
96
- executionPayloadAvailability: boolean[];
97
- latestExecutionPayloadBid: ExecutionPayloadBid;
98
- getBuilder(index: BuilderIndex): gloas.Builder;
99
- canBuilderCoverBid(builderIndex: BuilderIndex, bidAmount: number): boolean;
100
- validatorPTCCommitteeIndex(validatorIndex: ValidatorIndex, slot: Slot): number;
101
-
102
77
  // Shuffling and committees
103
78
  getShufflingAtEpoch(epoch: Epoch): EpochShuffling;
104
79
  // Decision roots
@@ -110,19 +85,11 @@ export interface IBeaconStateView {
110
85
  getCurrentShuffling(): EpochShuffling;
111
86
  getNextShuffling(): EpochShuffling;
112
87
 
113
- // utils: proposers, anchor checkpoint
88
+ // Proposer shuffling
114
89
  previousProposers: ValidatorIndex[] | null;
115
90
  currentProposers: ValidatorIndex[];
116
91
  nextProposers: ValidatorIndex[];
117
92
  getBeaconProposer(slot: Slot): ValidatorIndex;
118
- computeAnchorCheckpoint(): {checkpoint: phase0.Checkpoint; blockHeader: phase0.BeaconBlockHeader};
119
-
120
- // Sync committees
121
- currentSyncCommittee: altair.SyncCommittee;
122
- nextSyncCommittee: altair.SyncCommittee;
123
- currentSyncCommitteeIndexed: SyncCommitteeCache;
124
- syncProposerReward: number;
125
- getIndexedSyncCommitteeAtEpoch(epoch: Epoch): SyncCommitteeCache;
126
93
 
127
94
  // Validators and balances
128
95
  effectiveBalanceIncrements: EffectiveBalanceIncrements;
@@ -138,28 +105,10 @@ export interface IBeaconStateView {
138
105
  getAllValidators(): phase0.Validator[];
139
106
  getAllBalances(): number[];
140
107
 
141
- // Merge
142
- isExecutionStateType: boolean;
143
- isMergeTransitionComplete: boolean;
144
- // TODO this should go away (or rather only need block)
145
- isExecutionEnabled(block: BeaconBlock | BlindedBeaconBlock): boolean;
146
-
147
- // Block production
148
- getExpectedWithdrawals(): {
149
- expectedWithdrawals: capella.Withdrawal[];
150
- processedBuilderWithdrawalsCount: number;
151
- processedPartialWithdrawalsCount: number;
152
- processedValidatorSweepCount: number;
153
- };
154
-
155
108
  // API
156
109
  proposerRewards: RewardCache;
157
110
  computeBlockRewards(block: BeaconBlock, proposerRewards?: RewardCache): Promise<rewards.BlockRewards>;
158
111
  computeAttestationsRewards(validatorIds?: (ValidatorIndex | string)[]): Promise<rewards.AttestationsRewards>;
159
- computeSyncCommitteeRewards(
160
- block: BeaconBlock,
161
- validatorIds: (ValidatorIndex | string)[]
162
- ): Promise<rewards.SyncCommitteeRewards>;
163
112
  getLatestWeakSubjectivityCheckpointEpoch(): Epoch;
164
113
 
165
114
  // Validation
@@ -171,7 +120,6 @@ export interface IBeaconStateView {
171
120
 
172
121
  // Proofs
173
122
  getFinalizedRootProof(): Uint8Array[];
174
- getSyncCommitteesWitness(): SyncCommitteeWitness;
175
123
  getSingleProof(gindex: bigint): Uint8Array[];
176
124
  createMultiProof(descriptor: Uint8Array): CompactMultiProof;
177
125
 
@@ -180,6 +128,7 @@ export interface IBeaconStateView {
180
128
  justifiedCheckpoint: phase0.Checkpoint;
181
129
  finalizedCheckpoint: phase0.Checkpoint;
182
130
  };
131
+ computeAnchorCheckpoint(): {checkpoint: phase0.Checkpoint; blockHeader: phase0.BeaconBlockHeader};
183
132
 
184
133
  // this is for backward compatible
185
134
  clonedCount: number;
@@ -190,6 +139,7 @@ export interface IBeaconStateView {
190
139
 
191
140
  // Serialization
192
141
  loadOtherState(stateBytes: Uint8Array, seedValidatorsBytes?: Uint8Array): IBeaconStateView;
142
+ toValue(): BeaconState;
193
143
  serialize(): Uint8Array;
194
144
  serializedSize(): number;
195
145
  serializeToBytes(output: ByteViews, offset: number): number;
@@ -210,8 +160,132 @@ export interface IBeaconStateView {
210
160
  epochTransitionCacheOpts?: EpochTransitionCacheOpts & {dontTransferCache?: boolean},
211
161
  modules?: StateTransitionModules
212
162
  ): IBeaconStateView;
163
+ }
164
+
165
+ /** Altair+ state fields — use isStatePostAltair() guard */
166
+ export interface IBeaconStateViewAltair extends IBeaconStateView {
167
+ forkName: ForkPostAltair;
168
+ previousEpochParticipation: Uint8Array;
169
+ currentEpochParticipation: Uint8Array;
170
+ getPreviousEpochParticipation(validatorIndex: ValidatorIndex): number;
171
+ getCurrentEpochParticipation(validatorIndex: ValidatorIndex): number;
172
+ currentSyncCommittee: altair.SyncCommittee;
173
+ nextSyncCommittee: altair.SyncCommittee;
174
+ currentSyncCommitteeIndexed: SyncCommitteeCache;
175
+ syncProposerReward: number;
176
+ getIndexedSyncCommitteeAtEpoch(epoch: Epoch): SyncCommitteeCache;
177
+ /** Get indexed sync committee with slot+1 offset for duty lookups */
178
+ getIndexedSyncCommittee(slot: Slot): SyncCommitteeCache;
179
+ computeSyncCommitteeRewards(
180
+ block: BeaconBlock,
181
+ validatorIds: (ValidatorIndex | string)[]
182
+ ): Promise<rewards.SyncCommitteeRewards>;
183
+ getSyncCommitteesWitness(): SyncCommitteeWitness;
184
+ }
185
+
186
+ /** Bellatrix+ state fields — use isStatePostBellatrix() guard */
187
+ export interface IBeaconStateViewBellatrix extends IBeaconStateViewAltair {
188
+ forkName: ForkPostBellatrix;
189
+ latestExecutionPayloadHeader: ExecutionPayloadHeader;
190
+ /**
191
+ * Cross-fork accessor for the execution block hash of the most recently included payload.
192
+ * Pre-gloas: returns latestExecutionPayloadHeader.blockHash (bellatrix–fulu).
193
+ * Gloas+: returns the dedicated latestBlockHash state field (EIP-7732).
194
+ * Throws before bellatrix.
195
+ */
196
+ latestBlockHash: Bytes32;
197
+ /**
198
+ * The execution block number of the most recently included payload.
199
+ * Named payloadBlockNumber (not latestBlockNumber) to mirror ExecutionPayloadHeader.blockNumber pre-gloas.
200
+ * Only available from bellatrix through fulu — not tracked on BeaconState in gloas+ (EIP-7732).
201
+ * Throws before bellatrix and from gloas onwards.
202
+ */
203
+ payloadBlockNumber: number;
204
+ isExecutionStateType: boolean;
205
+ isMergeTransitionComplete: boolean;
206
+ isExecutionEnabled(block: BeaconBlock | BlindedBeaconBlock): boolean;
207
+ }
208
+
209
+ /** Capella+ state fields — use isStatePostCapella() guard */
210
+ export interface IBeaconStateViewCapella extends IBeaconStateViewBellatrix {
211
+ forkName: ForkPostCapella;
212
+ historicalSummaries: capella.HistoricalSummaries;
213
+ getExpectedWithdrawals(): {
214
+ expectedWithdrawals: capella.Withdrawal[];
215
+ processedBuilderWithdrawalsCount: number;
216
+ processedPartialWithdrawalsCount: number;
217
+ processedBuildersSweepCount: number;
218
+ processedValidatorSweepCount: number;
219
+ };
220
+ }
221
+
222
+ /** Deneb+ state — no new state-view fields; placeholder for fork completeness and isStatePostDeneb() narrowing */
223
+ export interface IBeaconStateViewDeneb extends IBeaconStateViewCapella {
224
+ forkName: ForkPostDeneb;
225
+ }
226
+
227
+ /** Electra+ state fields — use isStatePostElectra() guard */
228
+ export interface IBeaconStateViewElectra extends IBeaconStateViewDeneb {
229
+ forkName: ForkPostElectra;
230
+ pendingDeposits: electra.PendingDeposits;
231
+ pendingDepositsCount: number;
232
+ pendingPartialWithdrawals: electra.PendingPartialWithdrawals;
233
+ pendingPartialWithdrawalsCount: number;
234
+ pendingConsolidations: electra.PendingConsolidations;
235
+ pendingConsolidationsCount: number;
236
+ }
237
+
238
+ /** Fulu+ state fields — use isStatePostFulu() guard */
239
+ export interface IBeaconStateViewFulu extends IBeaconStateViewElectra {
240
+ forkName: ForkPostFulu;
241
+ proposerLookahead: fulu.ProposerLookahead;
242
+ }
243
+
244
+ /** Gloas+ state fields — use isStatePostGloas() guard */
245
+ export interface IBeaconStateViewGloas extends IBeaconStateViewFulu {
246
+ forkName: ForkPostGloas;
247
+ executionPayloadAvailability: BitArray;
248
+ latestExecutionPayloadBid: ExecutionPayloadBid;
249
+ getBuilder(index: BuilderIndex): gloas.Builder;
250
+ canBuilderCoverBid(builderIndex: BuilderIndex, bidAmount: number): boolean;
251
+ getIndexInPayloadTimelinessCommittee(validatorIndex: ValidatorIndex, slot: Slot): number;
213
252
  processExecutionPayloadEnvelope(
214
253
  signedEnvelope: gloas.SignedExecutionPayloadEnvelope,
215
254
  opts?: ProcessExecutionPayloadEnvelopeOpts
216
255
  ): IBeaconStateView;
217
256
  }
257
+
258
+ /**
259
+ * Type constraint for the concrete BeaconStateView class.
260
+ * Requires all fields from the latest fork interface (IBeaconStateViewGloas) but keeps
261
+ * forkName as ForkName since the class wraps any fork's state.
262
+ * Sub-interfaces retain their narrowed forkName discriminants for caller-side type guards.
263
+ */
264
+ export type IBeaconStateViewLatestFork = Omit<IBeaconStateViewGloas, "forkName"> & {forkName: ForkName};
265
+ export function isStatePostAltair(state: IBeaconStateView): state is IBeaconStateViewAltair {
266
+ return isForkPostAltair(state.forkName);
267
+ }
268
+
269
+ export function isStatePostBellatrix(state: IBeaconStateView): state is IBeaconStateViewBellatrix {
270
+ return isForkPostBellatrix(state.forkName);
271
+ }
272
+
273
+ export function isStatePostCapella(state: IBeaconStateView): state is IBeaconStateViewCapella {
274
+ return isForkPostCapella(state.forkName);
275
+ }
276
+
277
+ export function isStatePostDeneb(state: IBeaconStateView): state is IBeaconStateViewDeneb {
278
+ return isForkPostDeneb(state.forkName);
279
+ }
280
+
281
+ export function isStatePostElectra(state: IBeaconStateView): state is IBeaconStateViewElectra {
282
+ return isForkPostElectra(state.forkName);
283
+ }
284
+
285
+ export function isStatePostFulu(state: IBeaconStateView): state is IBeaconStateViewFulu {
286
+ return isForkPostFulu(state.forkName);
287
+ }
288
+
289
+ export function isStatePostGloas(state: IBeaconStateView): state is IBeaconStateViewGloas {
290
+ return isForkPostGloas(state.forkName);
291
+ }
@@ -0,0 +1,78 @@
1
+ import {BeaconConfig} from "@lodestar/config";
2
+ import {PubkeyCache, createPubkeyCache} from "../cache/pubkeyCache.js";
3
+ import {createCachedBeaconState} from "../cache/stateCache.js";
4
+ import {BeaconStateAllForks} from "../cache/types.js";
5
+ import {getStateTypeFromBytes} from "../util/sszBytes.js";
6
+ import {BeaconStateView} from "./beaconStateView.js";
7
+ import {IBeaconStateView} from "./interface.js";
8
+
9
+ // ---- createBeaconStateView (startup path) ----
10
+
11
+ type NodeJSOpts = {
12
+ useNative: false;
13
+ anchorState: BeaconStateAllForks;
14
+ config: BeaconConfig;
15
+ pubkeyCache: PubkeyCache;
16
+ };
17
+
18
+ type NativeOpts = {
19
+ useNative: true;
20
+ stateBytes: Uint8Array;
21
+ };
22
+
23
+ /**
24
+ * Create a BeaconStateView from a pre-deserialized state. Used at node startup.
25
+ *
26
+ * The caller is responsible for creating and populating `pubkeyCache` (it is also
27
+ * passed separately to BeaconNode.init, so it must live outside this factory).
28
+ *
29
+ * Set `useNative: true` to use the native (Zig) implementation once available.
30
+ */
31
+ export function createBeaconStateView(opts: NodeJSOpts | NativeOpts): IBeaconStateView {
32
+ if (opts.useNative) {
33
+ throw new Error("Native (Zig) BeaconStateView not yet implemented");
34
+ }
35
+ const {anchorState, config, pubkeyCache} = opts;
36
+ const cachedState = createCachedBeaconState(anchorState, {config, pubkeyCache}, {skipSyncPubkeys: true});
37
+ return new BeaconStateView(cachedState);
38
+ }
39
+
40
+ // ---- createBeaconStateViewForHistoricalRegen (regen path) ----
41
+
42
+ // Reused across all historical state regen calls in the worker thread
43
+ const pubkeyCacheRegen = createPubkeyCache();
44
+
45
+ function syncPubkeyCache(state: BeaconStateAllForks, pubkeyCache: PubkeyCache): void {
46
+ const newCount = state.validators.length;
47
+ for (let i = pubkeyCache.size; i < newCount; i++) {
48
+ const pubkey = state.validators.getReadonly(i).pubkey;
49
+ pubkeyCache.set(i, pubkey);
50
+ }
51
+ }
52
+
53
+ type RegenNodeJSOpts = {
54
+ useNative: false;
55
+ config: BeaconConfig;
56
+ stateBytes: Uint8Array;
57
+ };
58
+
59
+ type RegenNativeOpts = {
60
+ useNative: true;
61
+ stateBytes: Uint8Array;
62
+ };
63
+
64
+ /**
65
+ * Create a BeaconStateView from raw SSZ bytes. Used in the historical state regen worker thread.
66
+ *
67
+ * Set `useNative: true` to use the native (Zig) implementation once available.
68
+ */
69
+ export function createBeaconStateViewForHistoricalRegen(opts: RegenNodeJSOpts | RegenNativeOpts): IBeaconStateView {
70
+ if (opts.useNative) {
71
+ throw new Error("Native (Zig) BeaconStateView not yet implemented");
72
+ }
73
+ const {config, stateBytes} = opts;
74
+ const state = getStateTypeFromBytes(config, stateBytes).deserializeToViewDU(stateBytes);
75
+ syncPubkeyCache(state, pubkeyCacheRegen);
76
+ const cachedState = createCachedBeaconState(state, {config, pubkeyCache: pubkeyCacheRegen}, {skipSyncPubkeys: true});
77
+ return new BeaconStateView(cachedState);
78
+ }
@@ -5,4 +5,4 @@ import {fileURLToPath} from "node:url";
5
5
  // Solutions: https://stackoverflow.com/questions/46745014/alternative-for-dirname-in-node-js-when-using-es6-modules
6
6
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
7
 
8
- export const testCachePath = path.join(__dirname, "../../test/test-cache");
8
+ export const testCachePath = path.join(__dirname, "../../test-cache");