@lodestar/state-transition 1.41.0-dev.a35cbde8b3 → 1.41.0-dev.aeb5a213ee

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/lib/cache/stateCache.d.ts +1 -1
  2. package/lib/cache/stateCache.d.ts.map +1 -1
  3. package/lib/index.d.ts +2 -0
  4. package/lib/index.d.ts.map +1 -1
  5. package/lib/index.js +1 -0
  6. package/lib/index.js.map +1 -1
  7. package/lib/lightClient/proofs.d.ts +10 -0
  8. package/lib/lightClient/proofs.d.ts.map +1 -0
  9. package/lib/lightClient/proofs.js +63 -0
  10. package/lib/lightClient/proofs.js.map +1 -0
  11. package/lib/lightClient/types.d.ts +34 -0
  12. package/lib/lightClient/types.d.ts.map +1 -0
  13. package/lib/lightClient/types.js +2 -0
  14. package/lib/lightClient/types.js.map +1 -0
  15. package/lib/stateView/beaconStateView.d.ts +144 -0
  16. package/lib/stateView/beaconStateView.d.ts.map +1 -0
  17. package/lib/stateView/beaconStateView.js +498 -0
  18. package/lib/stateView/beaconStateView.js.map +1 -0
  19. package/lib/stateView/index.d.ts +3 -0
  20. package/lib/stateView/index.d.ts.map +1 -0
  21. package/lib/stateView/index.js +3 -0
  22. package/lib/stateView/index.js.map +1 -0
  23. package/lib/stateView/interface.d.ts +118 -0
  24. package/lib/stateView/interface.d.ts.map +1 -0
  25. package/lib/stateView/interface.js +2 -0
  26. package/lib/stateView/interface.js.map +1 -0
  27. package/lib/util/weakSubjectivity.js +1 -1
  28. package/lib/util/weakSubjectivity.js.map +1 -1
  29. package/package.json +7 -7
  30. package/src/cache/stateCache.ts +1 -1
  31. package/src/index.ts +2 -0
  32. package/src/lightClient/proofs.ts +83 -0
  33. package/src/lightClient/types.ts +33 -0
  34. package/src/stateView/beaconStateView.ts +746 -0
  35. package/src/stateView/index.ts +2 -0
  36. package/src/stateView/interface.ts +196 -0
  37. package/src/util/weakSubjectivity.ts +1 -1
@@ -0,0 +1,746 @@
1
+ import {CompactMultiProof, ProofType, Tree, createProof} from "@chainsafe/persistent-merkle-tree";
2
+ import {PubkeyIndexMap} from "@chainsafe/pubkey-index-map";
3
+ import {ByteViews} from "@chainsafe/ssz";
4
+ import {BeaconConfig} from "@lodestar/config";
5
+ import {ForkSeq, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
6
+ import {
7
+ BeaconBlock,
8
+ BlindedBeaconBlock,
9
+ BuilderIndex,
10
+ Bytes32,
11
+ Epoch,
12
+ ExecutionPayloadBid,
13
+ ExecutionPayloadHeader,
14
+ Root,
15
+ RootHex,
16
+ SignedBeaconBlock,
17
+ SignedBlindedBeaconBlock,
18
+ Slot,
19
+ SyncCommittee,
20
+ ValidatorIndex,
21
+ capella,
22
+ electra,
23
+ fulu,
24
+ getValidatorStatus,
25
+ gloas,
26
+ mapToGeneralStatus,
27
+ phase0,
28
+ rewards,
29
+ } from "@lodestar/types";
30
+ import {Checkpoint, Fork} from "@lodestar/types/phase0";
31
+ import {VoluntaryExitValidity, getVoluntaryExitValidity} from "../block/processVoluntaryExit.js";
32
+ import {getExpectedWithdrawals} from "../block/processWithdrawals.js";
33
+ import {EffectiveBalanceIncrements} from "../cache/effectiveBalanceIncrements.js";
34
+ import {EpochTransitionCacheOpts} from "../cache/epochTransitionCache.js";
35
+ import {RewardCache} from "../cache/rewardCache.js";
36
+ import {
37
+ CachedBeaconStateAllForks,
38
+ CachedBeaconStateAltair,
39
+ CachedBeaconStateCapella,
40
+ CachedBeaconStateElectra,
41
+ CachedBeaconStateExecutions,
42
+ CachedBeaconStateFulu,
43
+ CachedBeaconStateGloas,
44
+ createCachedBeaconState,
45
+ isStateValidatorsNodesPopulated,
46
+ } from "../cache/stateCache.js";
47
+ import {SyncCommitteeCache} from "../cache/syncCommitteeCache.js";
48
+ import {BeaconStateAllForks} from "../cache/types.js";
49
+ import {computeUnrealizedCheckpoints} from "../epoch/computeUnrealizedCheckpoints.js";
50
+ import {getFinalizedRootProof, getSyncCommitteesWitness} from "../lightClient/proofs.js";
51
+ import {SyncCommitteeWitness} from "../lightClient/types.js";
52
+ import {computeAttestationsRewards} from "../rewards/attestationsRewards.js";
53
+ import {computeBlockRewards} from "../rewards/blockRewards.js";
54
+ import {computeSyncCommitteeRewards} from "../rewards/syncCommitteeRewards.js";
55
+ import {StateTransitionModules, StateTransitionOpts, processSlots, stateTransition} from "../stateTransition.js";
56
+ import {getEffectiveBalanceIncrementsZeroInactive} from "../util/balance.js";
57
+ import {getBlockRootAtSlot} from "../util/blockRoot.js";
58
+ import {computeAnchorCheckpoint} from "../util/computeAnchorCheckpoint.js";
59
+ import {computeEpochAtSlot, computeStartSlotAtEpoch} from "../util/epoch.js";
60
+ import {EpochShuffling} from "../util/epochShuffling.js";
61
+ import {isExecutionEnabled, isExecutionStateType, isMergeTransitionComplete} from "../util/execution.js";
62
+ import {canBuilderCoverBid} from "../util/gloas.js";
63
+ import {loadState} from "../util/loadState/loadState.js";
64
+ import {getRandaoMix} from "../util/seed.js";
65
+ import {getStateTypeFromBytes} from "../util/sszBytes.js";
66
+ import {getLatestWeakSubjectivityCheckpointEpoch} from "../util/weakSubjectivity.js";
67
+ import {IBeaconStateView} from "./interface.js";
68
+
69
+ export class BeaconStateView implements IBeaconStateView {
70
+ private readonly config: BeaconConfig;
71
+ // Cached values extracted from the tree
72
+ // phase0
73
+ private _fork: Fork | null = null;
74
+ private _latestBlockHeader: phase0.BeaconBlockHeader | null = null;
75
+ // altair
76
+ private _currentSyncCommittee: SyncCommittee | null = null;
77
+ private _nextSyncCommittee: SyncCommittee | null = null;
78
+ private _previousEpochParticipation: number[] | null = null;
79
+ private _currentEpochParticipation: number[] | null = null;
80
+ // bellatrix
81
+ private _latestExecutionPayloadHeader: ExecutionPayloadHeader | null = null;
82
+ // capella
83
+ private _historicalSummaries: capella.HistoricalSummaries | null = null;
84
+ // electra
85
+ private _pendingPartialWithdrawals: electra.PendingPartialWithdrawals | null = null;
86
+ private _pendingConsolidations: electra.PendingConsolidations | null = null;
87
+ private _pendingDeposits: electra.PendingDeposits | null = null;
88
+ // fulu
89
+ private _proposerLookahead: fulu.ProposerLookahead | null = null;
90
+ // gloas
91
+ private _executionPayloadAvailability: boolean[] | null = null;
92
+ private _latestExecutionPayloadBid: ExecutionPayloadBid | null = null;
93
+
94
+ constructor(readonly cachedState: CachedBeaconStateAllForks) {
95
+ this.config = cachedState.config;
96
+ }
97
+
98
+ // phase0
99
+
100
+ get slot(): number {
101
+ return this.cachedState.slot;
102
+ }
103
+
104
+ get fork(): Fork {
105
+ if (this._fork === null) {
106
+ this._fork = this.cachedState.fork.toValue();
107
+ }
108
+ return this._fork;
109
+ }
110
+
111
+ get epoch(): number {
112
+ return computeEpochAtSlot(this.slot);
113
+ }
114
+
115
+ get genesisTime(): number {
116
+ return this.cachedState.genesisTime;
117
+ }
118
+
119
+ get genesisValidatorsRoot(): Root {
120
+ return this.cachedState.genesisValidatorsRoot;
121
+ }
122
+
123
+ get eth1Data(): phase0.Eth1Data {
124
+ return this.cachedState.eth1Data;
125
+ }
126
+
127
+ get latestBlockHeader(): phase0.BeaconBlockHeader {
128
+ if (this._latestBlockHeader === null) {
129
+ this._latestBlockHeader = this.cachedState.latestBlockHeader.toValue();
130
+ }
131
+ return this._latestBlockHeader;
132
+ }
133
+
134
+ get previousJustifiedCheckpoint(): Checkpoint {
135
+ return this.cachedState.previousJustifiedCheckpoint;
136
+ }
137
+
138
+ get currentJustifiedCheckpoint(): Checkpoint {
139
+ return this.cachedState.currentJustifiedCheckpoint;
140
+ }
141
+
142
+ get finalizedCheckpoint(): Checkpoint {
143
+ return this.cachedState.finalizedCheckpoint;
144
+ }
145
+
146
+ getBlockRootAtSlot(slot: Slot): Root {
147
+ return getBlockRootAtSlot(this.cachedState, slot);
148
+ }
149
+
150
+ getBlockRootAtEpoch(epoch: Epoch): Root {
151
+ return this.getBlockRootAtSlot(computeStartSlotAtEpoch(epoch));
152
+ }
153
+
154
+ getStateRootAtSlot(slot: Slot): Root {
155
+ return this.cachedState.stateRoots.get(slot % SLOTS_PER_HISTORICAL_ROOT);
156
+ }
157
+
158
+ getRandaoMix(epoch: Epoch): Bytes32 {
159
+ return getRandaoMix(this.cachedState, epoch);
160
+ }
161
+
162
+ get previousEpochParticipation(): number[] {
163
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.altair) {
164
+ throw new Error("previousEpochParticipation is not available before Altair");
165
+ }
166
+
167
+ if (this._previousEpochParticipation === null) {
168
+ this._previousEpochParticipation = (
169
+ this.cachedState as CachedBeaconStateAltair
170
+ ).previousEpochParticipation.toValue();
171
+ }
172
+
173
+ return this._previousEpochParticipation;
174
+ }
175
+
176
+ // altair
177
+
178
+ get currentEpochParticipation(): number[] {
179
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.altair) {
180
+ throw new Error("currentEpochParticipation is not available before Altair");
181
+ }
182
+
183
+ if (this._currentEpochParticipation === null) {
184
+ this._currentEpochParticipation = (
185
+ this.cachedState as CachedBeaconStateAltair
186
+ ).currentEpochParticipation.toValue();
187
+ }
188
+
189
+ return this._currentEpochParticipation;
190
+ }
191
+
192
+ // bellatrix
193
+
194
+ get latestExecutionPayloadHeader(): ExecutionPayloadHeader {
195
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.bellatrix) {
196
+ throw new Error("latestExecutionPayloadHeader is not available before Bellatrix");
197
+ }
198
+
199
+ if (this._latestExecutionPayloadHeader === null) {
200
+ this._latestExecutionPayloadHeader = (
201
+ this.cachedState as CachedBeaconStateExecutions
202
+ ).latestExecutionPayloadHeader.toValue();
203
+ }
204
+
205
+ return this._latestExecutionPayloadHeader;
206
+ }
207
+
208
+ // capella
209
+
210
+ get historicalSummaries(): capella.HistoricalSummaries {
211
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.capella) {
212
+ throw new Error("Historical summaries are not supported before Capella");
213
+ }
214
+
215
+ if (this._historicalSummaries === null) {
216
+ this._historicalSummaries = (this.cachedState as CachedBeaconStateCapella).historicalSummaries.toValue();
217
+ }
218
+
219
+ return this._historicalSummaries;
220
+ }
221
+
222
+ // electra
223
+
224
+ get pendingDeposits(): electra.PendingDeposits {
225
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.electra) {
226
+ throw new Error("Pending deposits are not supported before Electra");
227
+ }
228
+
229
+ if (this._pendingDeposits === null) {
230
+ this._pendingDeposits = (this.cachedState as CachedBeaconStateElectra).pendingDeposits.toValue();
231
+ }
232
+
233
+ return this._pendingDeposits;
234
+ }
235
+
236
+ get pendingDepositsCount(): number {
237
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.electra) {
238
+ throw new Error("Pending deposits are not supported before Electra");
239
+ }
240
+
241
+ return (this.cachedState as CachedBeaconStateElectra).pendingDeposits.length;
242
+ }
243
+
244
+ get pendingPartialWithdrawals(): electra.PendingPartialWithdrawals {
245
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.electra) {
246
+ throw new Error("Pending partial withdrawals are not supported before Electra");
247
+ }
248
+
249
+ if (this._pendingPartialWithdrawals === null) {
250
+ this._pendingPartialWithdrawals = (
251
+ this.cachedState as CachedBeaconStateElectra
252
+ ).pendingPartialWithdrawals.toValue();
253
+ }
254
+
255
+ return this._pendingPartialWithdrawals;
256
+ }
257
+
258
+ get pendingPartialWithdrawalsCount(): number {
259
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.electra) {
260
+ throw new Error("Pending partial withdrawals are not supported before Electra");
261
+ }
262
+
263
+ return (this.cachedState as CachedBeaconStateElectra).pendingPartialWithdrawals.length;
264
+ }
265
+
266
+ get pendingConsolidations(): electra.PendingConsolidations {
267
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.electra) {
268
+ throw new Error("Pending consolidations are not supported before Electra");
269
+ }
270
+
271
+ if (this._pendingConsolidations === null) {
272
+ this._pendingConsolidations = (this.cachedState as CachedBeaconStateElectra).pendingConsolidations.toValue();
273
+ }
274
+
275
+ return this._pendingConsolidations;
276
+ }
277
+
278
+ get pendingConsolidationsCount(): number {
279
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.electra) {
280
+ throw new Error("Pending consolidations are not supported before Electra");
281
+ }
282
+
283
+ return (this.cachedState as CachedBeaconStateElectra).pendingConsolidations.length;
284
+ }
285
+
286
+ // fulu
287
+
288
+ get proposerLookahead(): fulu.ProposerLookahead {
289
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.fulu) {
290
+ throw new Error("Proposer lookahead is not supported before Fulu");
291
+ }
292
+
293
+ if (this._proposerLookahead === null) {
294
+ this._proposerLookahead = (this.cachedState as CachedBeaconStateFulu).proposerLookahead.toValue();
295
+ }
296
+
297
+ return this._proposerLookahead;
298
+ }
299
+
300
+ // gloas
301
+
302
+ get executionPayloadAvailability(): boolean[] {
303
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
304
+ throw new Error("executionPayloadAvailability is not available before GLOAS");
305
+ }
306
+
307
+ if (this._executionPayloadAvailability === null) {
308
+ this._executionPayloadAvailability = (this.cachedState as CachedBeaconStateGloas).executionPayloadAvailability
309
+ .toValue()
310
+ .toBoolArray();
311
+ }
312
+
313
+ return this._executionPayloadAvailability;
314
+ }
315
+
316
+ get latestExecutionPayloadBid(): ExecutionPayloadBid {
317
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
318
+ throw new Error("latestExecutionPayloadBid is not available before GLOAS");
319
+ }
320
+
321
+ if (this._latestExecutionPayloadBid === null) {
322
+ this._latestExecutionPayloadBid = (
323
+ this.cachedState as CachedBeaconStateGloas
324
+ ).latestExecutionPayloadBid.toValue();
325
+ }
326
+ return this._latestExecutionPayloadBid;
327
+ }
328
+
329
+ getBuilder(index: BuilderIndex): gloas.Builder {
330
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
331
+ throw new Error("Builders are not supported before GLOAS");
332
+ }
333
+
334
+ return (this.cachedState as CachedBeaconStateGloas).builders.getReadonly(index);
335
+ }
336
+
337
+ canBuilderCoverBid(builderIndex: BuilderIndex, bidAmount: number): boolean {
338
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
339
+ throw new Error("Builders are not supported before GLOAS");
340
+ }
341
+
342
+ return canBuilderCoverBid(this.cachedState as CachedBeaconStateGloas, builderIndex, bidAmount);
343
+ }
344
+
345
+ /**
346
+ * Return the index of the validator in the PTC committee for the given slot.
347
+ * return -1 if validator is not in the PTC committee for the given slot.
348
+ */
349
+ validatorPTCCommitteeIndex(validatorIndex: ValidatorIndex, slot: Slot): number {
350
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
351
+ throw new Error("PTC committees are not supported before GLOAS");
352
+ }
353
+
354
+ const ptcCommittee = (this.cachedState as CachedBeaconStateGloas).epochCtx.getPayloadTimelinessCommittee(slot);
355
+ return ptcCommittee.indexOf(validatorIndex);
356
+ }
357
+
358
+ // Shuffling and committees
359
+
360
+ getShufflingAtEpoch(epoch: Epoch): EpochShuffling {
361
+ return this.cachedState.epochCtx.getShufflingAtEpoch(epoch);
362
+ }
363
+
364
+ get previousDecisionRoot(): RootHex {
365
+ return this.cachedState.epochCtx.previousDecisionRoot;
366
+ }
367
+
368
+ get currentDecisionRoot(): RootHex {
369
+ return this.cachedState.epochCtx.currentDecisionRoot;
370
+ }
371
+
372
+ get nextDecisionRoot(): RootHex {
373
+ return this.cachedState.epochCtx.nextDecisionRoot;
374
+ }
375
+
376
+ getShufflingDecisionRoot(epoch: Epoch): RootHex {
377
+ return this.cachedState.epochCtx.getShufflingDecisionRoot(epoch);
378
+ }
379
+
380
+ getPreviousShuffling(): EpochShuffling {
381
+ return this.cachedState.epochCtx.previousShuffling;
382
+ }
383
+
384
+ getCurrentShuffling(): EpochShuffling {
385
+ return this.cachedState.epochCtx.currentShuffling;
386
+ }
387
+
388
+ getNextShuffling(): EpochShuffling {
389
+ return this.cachedState.epochCtx.nextShuffling;
390
+ }
391
+
392
+ // Proposer shuffling
393
+
394
+ get previousProposers(): ValidatorIndex[] | null {
395
+ return this.cachedState.epochCtx.proposersPrevEpoch;
396
+ }
397
+
398
+ get currentProposers(): ValidatorIndex[] {
399
+ return this.cachedState.epochCtx.getBeaconProposers();
400
+ }
401
+
402
+ get nextProposers(): ValidatorIndex[] {
403
+ return this.cachedState.epochCtx.getBeaconProposersNextEpoch();
404
+ }
405
+
406
+ getBeaconProposer(slot: number): ValidatorIndex {
407
+ return this.cachedState.epochCtx.getBeaconProposer(slot);
408
+ }
409
+
410
+ computeAnchorCheckpoint(): {checkpoint: phase0.Checkpoint; blockHeader: phase0.BeaconBlockHeader} {
411
+ return computeAnchorCheckpoint(this.config, this.cachedState);
412
+ }
413
+
414
+ // Sync committees
415
+
416
+ get currentSyncCommittee(): SyncCommittee {
417
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.altair) {
418
+ throw new Error("currentSyncCommittee is not available before Altair");
419
+ }
420
+
421
+ if (this._currentSyncCommittee === null) {
422
+ this._currentSyncCommittee = (this.cachedState as CachedBeaconStateAltair).currentSyncCommittee.toValue();
423
+ }
424
+
425
+ return this._currentSyncCommittee;
426
+ }
427
+
428
+ get nextSyncCommittee(): SyncCommittee {
429
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.altair) {
430
+ throw new Error("nextSyncCommittee is not available before Altair");
431
+ }
432
+
433
+ if (this._nextSyncCommittee === null) {
434
+ this._nextSyncCommittee = (this.cachedState as CachedBeaconStateAltair).nextSyncCommittee.toValue();
435
+ }
436
+
437
+ return this._nextSyncCommittee;
438
+ }
439
+
440
+ get currentSyncCommitteeIndexed(): SyncCommitteeCache {
441
+ return this.cachedState.epochCtx.currentSyncCommitteeIndexed;
442
+ }
443
+
444
+ get syncProposerReward(): number {
445
+ return this.cachedState.epochCtx.syncProposerReward;
446
+ }
447
+
448
+ getIndexedSyncCommitteeAtEpoch(epoch: Epoch): SyncCommitteeCache {
449
+ return this.cachedState.epochCtx.getIndexedSyncCommitteeAtEpoch(epoch);
450
+ }
451
+
452
+ // Validators and balances
453
+
454
+ get effectiveBalanceIncrements(): EffectiveBalanceIncrements {
455
+ return this.cachedState.epochCtx.effectiveBalanceIncrements;
456
+ }
457
+
458
+ getEffectiveBalanceIncrementsZeroInactive(): EffectiveBalanceIncrements {
459
+ return getEffectiveBalanceIncrementsZeroInactive(this.cachedState);
460
+ }
461
+
462
+ getBalance(index: number): number {
463
+ return this.cachedState.balances.get(index);
464
+ }
465
+
466
+ getValidator(index: ValidatorIndex): phase0.Validator {
467
+ return this.cachedState.validators.getReadonly(index).toValue();
468
+ }
469
+
470
+ getValidatorsByStatus(statuses: Set<string>, currentEpoch: Epoch): phase0.Validator[] {
471
+ const validators: phase0.Validator[] = [];
472
+ const validatorsArr = this.cachedState.validators.getAllReadonlyValues();
473
+
474
+ for (const validator of validatorsArr) {
475
+ const validatorStatus = getValidatorStatus(validator, currentEpoch);
476
+ if (statuses.has(validatorStatus) || statuses.has(mapToGeneralStatus(validatorStatus))) {
477
+ validators.push(validator);
478
+ }
479
+ }
480
+ return validators;
481
+ }
482
+
483
+ get validatorCount(): number {
484
+ return this.cachedState.validators.length;
485
+ }
486
+
487
+ get activeValidatorCount(): number {
488
+ return this.cachedState.epochCtx.currentShuffling.activeIndices.length;
489
+ }
490
+
491
+ getAllValidators(): phase0.Validator[] {
492
+ return this.cachedState.validators.getAllReadonlyValues();
493
+ }
494
+
495
+ getAllBalances(): number[] {
496
+ return this.cachedState.balances.getAll();
497
+ }
498
+
499
+ // Merge
500
+
501
+ get isExecutionStateType(): boolean {
502
+ return this.config.getForkSeq(this.cachedState.slot) >= ForkSeq.bellatrix;
503
+ }
504
+
505
+ isExecutionEnabled(block: BeaconBlock | BlindedBeaconBlock): boolean {
506
+ if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.bellatrix) {
507
+ return false;
508
+ }
509
+
510
+ return isExecutionEnabled(this.cachedState as CachedBeaconStateExecutions, block);
511
+ }
512
+
513
+ get isMergeTransitionComplete(): boolean {
514
+ return isExecutionStateType(this.cachedState) && isMergeTransitionComplete(this.cachedState);
515
+ }
516
+
517
+ // Block production
518
+
519
+ getExpectedWithdrawals(): {
520
+ expectedWithdrawals: capella.Withdrawal[];
521
+ processedBuilderWithdrawalsCount: number;
522
+ processedPartialWithdrawalsCount: number;
523
+ processedBuildersSweepCount: number;
524
+ processedValidatorSweepCount: number;
525
+ } {
526
+ const fork = this.config.getForkSeq(this.cachedState.slot);
527
+ return getExpectedWithdrawals(
528
+ fork,
529
+ this.cachedState as CachedBeaconStateCapella | CachedBeaconStateElectra | CachedBeaconStateGloas
530
+ );
531
+ }
532
+
533
+ // API
534
+
535
+ get proposerRewards(): RewardCache {
536
+ return this.cachedState.proposerRewards;
537
+ }
538
+
539
+ async computeBlockRewards(block: BeaconBlock, proposerRewards?: RewardCache): Promise<rewards.BlockRewards> {
540
+ return computeBlockRewards(this.cachedState.config, block, this.cachedState, proposerRewards);
541
+ }
542
+
543
+ async computeAttestationsRewards(validatorIds?: (ValidatorIndex | string)[]): Promise<rewards.AttestationsRewards> {
544
+ return computeAttestationsRewards(
545
+ this.cachedState.config,
546
+ this.cachedState.epochCtx.pubkey2index,
547
+ this.cachedState,
548
+ validatorIds
549
+ );
550
+ }
551
+
552
+ async computeSyncCommitteeRewards(
553
+ block: BeaconBlock,
554
+ validatorIds: (ValidatorIndex | string)[]
555
+ ): Promise<rewards.SyncCommitteeRewards> {
556
+ return computeSyncCommitteeRewards(
557
+ this.cachedState.config,
558
+ this.cachedState.epochCtx.index2pubkey,
559
+ block,
560
+ this.cachedState,
561
+ validatorIds
562
+ );
563
+ }
564
+
565
+ getLatestWeakSubjectivityCheckpointEpoch(): Epoch {
566
+ return getLatestWeakSubjectivityCheckpointEpoch(this.config, this.cachedState);
567
+ }
568
+
569
+ // Validation
570
+
571
+ getVoluntaryExitValidity(
572
+ signedVoluntaryExit: phase0.SignedVoluntaryExit,
573
+ verifySignature = true
574
+ ): VoluntaryExitValidity {
575
+ const stateFork = this.config.getForkSeq(this.cachedState.slot);
576
+ return getVoluntaryExitValidity(stateFork, this.cachedState, signedVoluntaryExit, verifySignature);
577
+ }
578
+
579
+ isValidVoluntaryExit(signedVoluntaryExit: phase0.SignedVoluntaryExit, verifySignature: boolean): boolean {
580
+ return this.getVoluntaryExitValidity(signedVoluntaryExit, verifySignature) === VoluntaryExitValidity.valid;
581
+ }
582
+
583
+ // Proofs
584
+
585
+ getFinalizedRootProof(): Uint8Array[] {
586
+ return getFinalizedRootProof(this.cachedState);
587
+ }
588
+
589
+ getSyncCommitteesWitness(): SyncCommitteeWitness {
590
+ const fork = this.config.getForkName(this.cachedState.slot);
591
+ if (ForkSeq[fork] < ForkSeq.altair) {
592
+ throw new Error("Sync committees witness is not available before Altair");
593
+ }
594
+
595
+ return getSyncCommitteesWitness(fork, this.cachedState);
596
+ }
597
+
598
+ getSingleProof(gindex: bigint): Uint8Array[] {
599
+ return new Tree(this.cachedState.node).getSingleProof(gindex);
600
+ }
601
+
602
+ createMultiProof(descriptor: Uint8Array): CompactMultiProof {
603
+ const stateNode = this.cachedState.node;
604
+ return createProof(stateNode, {type: ProofType.compactMulti, descriptor}) as CompactMultiProof;
605
+ }
606
+
607
+ // Fork choice
608
+
609
+ computeUnrealizedCheckpoints(): {
610
+ justifiedCheckpoint: phase0.Checkpoint;
611
+ finalizedCheckpoint: phase0.Checkpoint;
612
+ } {
613
+ return computeUnrealizedCheckpoints(this.cachedState);
614
+ }
615
+
616
+ // this is for backward compatible
617
+
618
+ get clonedCount(): number {
619
+ return this.cachedState.clonedCount;
620
+ }
621
+
622
+ get clonedCountWithTransferCache(): number {
623
+ return this.cachedState.clonedCountWithTransferCache;
624
+ }
625
+
626
+ get createdWithTransferCache(): boolean {
627
+ return this.cachedState.createdWithTransferCache;
628
+ }
629
+
630
+ isStateValidatorsNodesPopulated(): boolean {
631
+ return isStateValidatorsNodesPopulated(this.cachedState);
632
+ }
633
+
634
+ // Serialization
635
+
636
+ loadOtherState(stateBytes: Uint8Array, seedValidatorsBytes?: Uint8Array): IBeaconStateView {
637
+ const {state} = loadState(this.config, this.cachedState, stateBytes, seedValidatorsBytes);
638
+
639
+ const cachedState = createCachedBeaconState(
640
+ state,
641
+ {
642
+ config: this.config,
643
+ // as of Feb 2026, it's not necessary to sync pubkey cache as it's shared across states in Lodestar
644
+ pubkey2index: this.cachedState.epochCtx.pubkey2index,
645
+ index2pubkey: this.cachedState.epochCtx.index2pubkey,
646
+ },
647
+ {
648
+ skipSyncPubkeys: true,
649
+ }
650
+ );
651
+
652
+ // load all cache in order for consumers (usually regen.getState()) to process blocks faster
653
+ cachedState.validators.getAllReadonlyValues();
654
+ cachedState.balances.getAll();
655
+
656
+ return new BeaconStateView(cachedState);
657
+ }
658
+
659
+ serialize(): Uint8Array {
660
+ return this.cachedState.serialize();
661
+ }
662
+
663
+ serializedSize(): number {
664
+ return this.cachedState.type.tree_serializedSize(this.cachedState.node);
665
+ }
666
+
667
+ serializeToBytes(output: ByteViews, offset: number): number {
668
+ return this.cachedState.serializeToBytes(output, offset);
669
+ }
670
+
671
+ serializeValidators(): Uint8Array {
672
+ return this.cachedState.validators.serialize();
673
+ }
674
+
675
+ serializedValidatorsSize(): number {
676
+ const type = this.cachedState.type.fields.validators;
677
+ return type.tree_serializedSize(this.cachedState.validators.node);
678
+ }
679
+
680
+ serializeValidatorsToBytes(output: ByteViews, offset: number): number {
681
+ return this.cachedState.validators.serializeToBytes(output, offset);
682
+ }
683
+
684
+ hashTreeRoot(): Uint8Array {
685
+ return this.cachedState.hashTreeRoot();
686
+ }
687
+
688
+ // State transition
689
+
690
+ stateTransition(
691
+ signedBlock: SignedBeaconBlock | SignedBlindedBeaconBlock,
692
+ options: StateTransitionOpts,
693
+ {metrics, validatorMonitor}: StateTransitionModules
694
+ ): IBeaconStateView {
695
+ const newState = stateTransition(this.cachedState, signedBlock, options, {metrics, validatorMonitor});
696
+ return new BeaconStateView(newState);
697
+ }
698
+
699
+ processSlots(
700
+ slot: Slot,
701
+ epochTransitionCacheOpts?: EpochTransitionCacheOpts & {dontTransferCache?: boolean},
702
+ modules?: StateTransitionModules
703
+ ): IBeaconStateView {
704
+ const newState = processSlots(this.cachedState, slot, epochTransitionCacheOpts, modules);
705
+ return new BeaconStateView(newState);
706
+ }
707
+ }
708
+
709
+ /**
710
+ * Create BeaconStateView for historical state regen, no need to sync pubkey cache there.
711
+ */
712
+ export function createBeaconStateViewForHistoricalRegen(
713
+ config: BeaconConfig,
714
+ stateBytes: Uint8Array
715
+ ): IBeaconStateView {
716
+ const state = getStateTypeFromBytes(config, stateBytes).deserializeToViewDU(stateBytes);
717
+
718
+ const pubkey2index = new PubkeyIndexMap();
719
+ syncPubkeyCache(state, pubkey2index);
720
+ const cachedState = createCachedBeaconState(
721
+ state,
722
+ {
723
+ config,
724
+ pubkey2index,
725
+ index2pubkey: [],
726
+ },
727
+ {
728
+ skipSyncPubkeys: true,
729
+ }
730
+ );
731
+
732
+ return new BeaconStateView(cachedState);
733
+ }
734
+
735
+ /**
736
+ * Populate a PubkeyIndexMap with any new entries based on a BeaconState
737
+ */
738
+ function syncPubkeyCache(state: BeaconStateAllForks, pubkey2index: PubkeyIndexMap): void {
739
+ // Get the validators sub tree once for all the loop
740
+
741
+ const newCount = state.validators.length;
742
+ for (let i = pubkey2index.size; i < newCount; i++) {
743
+ const pubkey = state.validators.getReadonly(i).pubkey;
744
+ pubkey2index.set(pubkey, i);
745
+ }
746
+ }