@lodestar/state-transition 1.42.0-dev.4411584fd8 → 1.42.0-dev.4e640bd2f7

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 (62) 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/block/processExecutionPayloadEnvelope.d.ts +3 -1
  9. package/lib/block/processExecutionPayloadEnvelope.d.ts.map +1 -1
  10. package/lib/block/processExecutionPayloadEnvelope.js +11 -28
  11. package/lib/block/processExecutionPayloadEnvelope.js.map +1 -1
  12. package/lib/signatureSets/executionPayloadEnvelope.d.ts +5 -1
  13. package/lib/signatureSets/executionPayloadEnvelope.d.ts.map +1 -1
  14. package/lib/signatureSets/executionPayloadEnvelope.js +10 -1
  15. package/lib/signatureSets/executionPayloadEnvelope.js.map +1 -1
  16. package/lib/signatureSets/index.d.ts +2 -2
  17. package/lib/signatureSets/index.d.ts.map +1 -1
  18. package/lib/signatureSets/index.js +1 -2
  19. package/lib/signatureSets/index.js.map +1 -1
  20. package/lib/slot/upgradeStateToAltair.d.ts.map +1 -1
  21. package/lib/slot/upgradeStateToAltair.js +2 -1
  22. package/lib/slot/upgradeStateToAltair.js.map +1 -1
  23. package/lib/stateView/beaconStateView.d.ts +8 -10
  24. package/lib/stateView/beaconStateView.d.ts.map +1 -1
  25. package/lib/stateView/beaconStateView.js +11 -34
  26. package/lib/stateView/beaconStateView.js.map +1 -1
  27. package/lib/stateView/index.d.ts +1 -0
  28. package/lib/stateView/index.d.ts.map +1 -1
  29. package/lib/stateView/index.js +1 -0
  30. package/lib/stateView/index.js.map +1 -1
  31. package/lib/stateView/interface.d.ts +14 -9
  32. package/lib/stateView/interface.d.ts.map +1 -1
  33. package/lib/stateView/stateViewFactory.d.ts +40 -0
  34. package/lib/stateView/stateViewFactory.d.ts.map +1 -0
  35. package/lib/stateView/stateViewFactory.js +46 -0
  36. package/lib/stateView/stateViewFactory.js.map +1 -0
  37. package/lib/testUtils/util.d.ts +23 -1
  38. package/lib/testUtils/util.d.ts.map +1 -1
  39. package/lib/testUtils/util.js +140 -19
  40. package/lib/testUtils/util.js.map +1 -1
  41. package/lib/util/rootCache.d.ts +2 -2
  42. package/lib/util/rootCache.d.ts.map +1 -1
  43. package/lib/util/rootCache.js +2 -3
  44. package/lib/util/rootCache.js.map +1 -1
  45. package/lib/util/shuffling.d.ts +2 -1
  46. package/lib/util/shuffling.d.ts.map +1 -1
  47. package/lib/util/shuffling.js +2 -2
  48. package/lib/util/shuffling.js.map +1 -1
  49. package/package.json +7 -7
  50. package/src/block/isValidIndexedAttestation.ts +2 -3
  51. package/src/block/processAttestationsAltair.ts +7 -4
  52. package/src/block/processExecutionPayloadEnvelope.ts +18 -35
  53. package/src/signatureSets/executionPayloadEnvelope.ts +26 -2
  54. package/src/signatureSets/index.ts +3 -4
  55. package/src/slot/upgradeStateToAltair.ts +2 -1
  56. package/src/stateView/beaconStateView.ts +27 -50
  57. package/src/stateView/index.ts +1 -0
  58. package/src/stateView/interface.ts +15 -6
  59. package/src/stateView/stateViewFactory.ts +78 -0
  60. package/src/testUtils/util.ts +158 -22
  61. package/src/util/rootCache.ts +4 -5
  62. package/src/util/shuffling.ts +5 -4
@@ -1,7 +1,8 @@
1
1
  import {CompactMultiProof} from "@chainsafe/persistent-merkle-tree";
2
- import {ByteViews} from "@chainsafe/ssz";
2
+ import {BitArray, ByteViews} from "@chainsafe/ssz";
3
3
  import {
4
4
  BeaconBlock,
5
+ BeaconState,
5
6
  BlindedBeaconBlock,
6
7
  BuilderIndex,
7
8
  Bytes32,
@@ -23,6 +24,7 @@ import {
23
24
  rewards,
24
25
  } from "@lodestar/types";
25
26
  import {Checkpoint, Fork} from "@lodestar/types/phase0";
27
+ import {ProcessExecutionPayloadEnvelopeOpts} from "../block/processExecutionPayloadEnvelope.js";
26
28
  import {VoluntaryExitValidity} from "../block/processVoluntaryExit.js";
27
29
  import {EffectiveBalanceIncrements} from "../cache/effectiveBalanceIncrements.js";
28
30
  import {EpochTransitionCacheOpts} from "../cache/epochTransitionCache.js";
@@ -92,11 +94,11 @@ export interface IBeaconStateView {
92
94
  proposerLookahead: fulu.ProposerLookahead;
93
95
 
94
96
  // gloas
95
- executionPayloadAvailability: boolean[];
97
+ executionPayloadAvailability: BitArray;
96
98
  latestExecutionPayloadBid: ExecutionPayloadBid;
97
99
  getBuilder(index: BuilderIndex): gloas.Builder;
98
100
  canBuilderCoverBid(builderIndex: BuilderIndex, bidAmount: number): boolean;
99
- validatorPTCCommitteeIndex(validatorIndex: ValidatorIndex, slot: Slot): number;
101
+ getIndexInPayloadTimelinessCommittee(validatorIndex: ValidatorIndex, slot: Slot): number;
100
102
 
101
103
  // Shuffling and committees
102
104
  getShufflingAtEpoch(epoch: Epoch): EpochShuffling;
@@ -109,12 +111,11 @@ export interface IBeaconStateView {
109
111
  getCurrentShuffling(): EpochShuffling;
110
112
  getNextShuffling(): EpochShuffling;
111
113
 
112
- // utils: proposers, anchor checkpoint
114
+ // Proposer shuffling
113
115
  previousProposers: ValidatorIndex[] | null;
114
116
  currentProposers: ValidatorIndex[];
115
117
  nextProposers: ValidatorIndex[];
116
118
  getBeaconProposer(slot: Slot): ValidatorIndex;
117
- computeAnchorCheckpoint(): {checkpoint: phase0.Checkpoint; blockHeader: phase0.BeaconBlockHeader};
118
119
 
119
120
  // Sync committees
120
121
  currentSyncCommittee: altair.SyncCommittee;
@@ -122,6 +123,8 @@ export interface IBeaconStateView {
122
123
  currentSyncCommitteeIndexed: SyncCommitteeCache;
123
124
  syncProposerReward: number;
124
125
  getIndexedSyncCommitteeAtEpoch(epoch: Epoch): SyncCommitteeCache;
126
+ /** Get indexed sync committee with slot+1 offset for duty lookups */
127
+ getIndexedSyncCommittee(slot: Slot): SyncCommitteeCache;
125
128
 
126
129
  // Validators and balances
127
130
  effectiveBalanceIncrements: EffectiveBalanceIncrements;
@@ -148,6 +151,7 @@ export interface IBeaconStateView {
148
151
  expectedWithdrawals: capella.Withdrawal[];
149
152
  processedBuilderWithdrawalsCount: number;
150
153
  processedPartialWithdrawalsCount: number;
154
+ processedBuildersSweepCount: number;
151
155
  processedValidatorSweepCount: number;
152
156
  };
153
157
 
@@ -179,6 +183,7 @@ export interface IBeaconStateView {
179
183
  justifiedCheckpoint: phase0.Checkpoint;
180
184
  finalizedCheckpoint: phase0.Checkpoint;
181
185
  };
186
+ computeAnchorCheckpoint(): {checkpoint: phase0.Checkpoint; blockHeader: phase0.BeaconBlockHeader};
182
187
 
183
188
  // this is for backward compatible
184
189
  clonedCount: number;
@@ -189,6 +194,7 @@ export interface IBeaconStateView {
189
194
 
190
195
  // Serialization
191
196
  loadOtherState(stateBytes: Uint8Array, seedValidatorsBytes?: Uint8Array): IBeaconStateView;
197
+ toValue(): BeaconState;
192
198
  serialize(): Uint8Array;
193
199
  serializedSize(): number;
194
200
  serializeToBytes(output: ByteViews, offset: number): number;
@@ -209,5 +215,8 @@ export interface IBeaconStateView {
209
215
  epochTransitionCacheOpts?: EpochTransitionCacheOpts & {dontTransferCache?: boolean},
210
216
  modules?: StateTransitionModules
211
217
  ): IBeaconStateView;
212
- processExecutionPayloadEnvelope(signedEnvelope: gloas.SignedExecutionPayloadEnvelope, verify: boolean): void;
218
+ processExecutionPayloadEnvelope(
219
+ signedEnvelope: gloas.SignedExecutionPayloadEnvelope,
220
+ opts?: ProcessExecutionPayloadEnvelopeOpts
221
+ ): IBeaconStateView;
213
222
  }
@@ -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
+ }
@@ -25,9 +25,11 @@ import {
25
25
  } from "../index.js";
26
26
  import {
27
27
  BeaconStateAltair,
28
+ BeaconStateElectra,
28
29
  BeaconStatePhase0,
29
30
  CachedBeaconStateAllForks,
30
31
  CachedBeaconStateAltair,
32
+ CachedBeaconStateElectra,
31
33
  CachedBeaconStatePhase0,
32
34
  } from "../types.js";
33
35
  import {getNextSyncCommittee} from "../util/syncCommittee.js";
@@ -41,6 +43,9 @@ let phase0SignedBlock: phase0.SignedBeaconBlock | null = null;
41
43
  let altairState: BeaconStateAltair | null = null;
42
44
  let altairCachedState23637: CachedBeaconStateAltair | null = null;
43
45
  let altairCachedState23638: CachedBeaconStateAltair | null = null;
46
+ let electraState: BeaconStateElectra | null = null;
47
+ let electraCachedState23637: CachedBeaconStateElectra | null = null;
48
+ let electraCachedState23638: CachedBeaconStateElectra | null = null;
44
49
 
45
50
  /**
46
51
  * Number of validators in prater is 210000 as of May 2021
@@ -84,10 +89,10 @@ export function getSecretKeyFromIndexCached(validatorIndex: number): SecretKey {
84
89
  return sk;
85
90
  }
86
91
 
87
- function getPubkeyCaches({pubkeysMod}: ReturnType<typeof getPubkeys>) {
92
+ function getPubkeyCaches({pubkeysMod}: ReturnType<typeof getPubkeys>, vc = numValidators) {
88
93
  // Manually sync pubkeys to prevent doing BLS opts 110_000 times
89
94
  const pubkeyCache = createPubkeyCache();
90
- for (let i = 0; i < numValidators; i++) {
95
+ for (let i = 0; i < vc; i++) {
91
96
  const pubkey = pubkeysMod[i % keypairsMod];
92
97
  pubkeyCache.set(i, pubkey);
93
98
  }
@@ -206,29 +211,80 @@ export function generatePerfTestCachedStateAltair(opts?: {
206
211
  goBackOneSlot: boolean;
207
212
  vc?: number;
208
213
  }): CachedBeaconStateAltair {
209
- const {pubkeys, pubkeysMod, pubkeysModObj} = getPubkeys(opts?.vc);
210
- const {pubkeyCache} = getPubkeyCaches({pubkeys, pubkeysMod, pubkeysModObj});
214
+ const vc = opts?.vc ?? numValidators;
215
+ const {pubkeys, pubkeysMod, pubkeysModObj} = getPubkeys(vc);
216
+ const {pubkeyCache} = getPubkeyCaches({pubkeys, pubkeysMod, pubkeysModObj}, vc);
211
217
 
212
218
  const altairConfig = createChainForkConfig({ALTAIR_FORK_EPOCH: 0});
213
219
 
214
220
  const origState = generatePerformanceStateAltair(pubkeys);
215
221
 
216
- if (!altairCachedState23637) {
222
+ // For non-default vc, generate fresh without caching to avoid accumulating large states in memory
223
+ const isDefaultVc = vc === numValidators;
224
+ let cachedState23637 = isDefaultVc ? altairCachedState23637 : null;
225
+ if (!cachedState23637) {
217
226
  const state = origState.clone();
218
227
  state.slot -= 1;
219
- altairCachedState23637 = createCachedBeaconState(state, {
228
+ cachedState23637 = createCachedBeaconState(state, {
220
229
  config: createBeaconConfig(altairConfig, state.genesisValidatorsRoot),
221
230
  pubkeyCache,
222
231
  });
232
+ if (isDefaultVc) altairCachedState23637 = cachedState23637;
233
+ }
234
+
235
+ let cachedState23638 = isDefaultVc ? altairCachedState23638 : null;
236
+ if (!cachedState23638) {
237
+ cachedState23638 = processSlots(cachedState23637, cachedState23637.slot + 1) as CachedBeaconStateAltair;
238
+ cachedState23638.slot += 1;
239
+ if (isDefaultVc) altairCachedState23638 = cachedState23638;
240
+ }
241
+
242
+ const resultingState = opts?.goBackOneSlot ? cachedState23637 : cachedState23638;
243
+
244
+ return resultingState.clone();
245
+ }
246
+
247
+ /**
248
+ * Warning: This function has side effects on the cached state
249
+ * The order in which the caches are populated is important and can cause stable tests to fail.
250
+ */
251
+ export function generatePerfTestCachedStateElectra(opts?: {
252
+ goBackOneSlot: boolean;
253
+ vc?: number;
254
+ }): CachedBeaconStateElectra {
255
+ const vc = opts?.vc ?? numValidators;
256
+ const {pubkeys, pubkeysMod, pubkeysModObj} = getPubkeys(vc);
257
+ const {pubkeyCache} = getPubkeyCaches({pubkeys, pubkeysMod, pubkeysModObj}, vc);
258
+
259
+ const electraConfig = createChainForkConfig({
260
+ ALTAIR_FORK_EPOCH: 0,
261
+ BELLATRIX_FORK_EPOCH: 0,
262
+ CAPELLA_FORK_EPOCH: 0,
263
+ DENEB_FORK_EPOCH: 0,
264
+ ELECTRA_FORK_EPOCH: 0,
265
+ });
266
+
267
+ const origState = generatePerformanceStateElectra(pubkeys);
268
+
269
+ // For non-default vc, generate fresh without caching to avoid accumulating large states in memory
270
+ const isDefaultVc = vc === numValidators;
271
+ let cachedState23637 = isDefaultVc ? electraCachedState23637 : null;
272
+ if (!cachedState23637) {
273
+ const state = origState.clone();
274
+ state.slot -= 1;
275
+ cachedState23637 = createCachedBeaconState(state, {
276
+ config: createBeaconConfig(electraConfig, state.genesisValidatorsRoot),
277
+ pubkeyCache,
278
+ });
279
+ if (isDefaultVc) electraCachedState23637 = cachedState23637;
223
280
  }
224
- if (!altairCachedState23638) {
225
- altairCachedState23638 = processSlots(
226
- altairCachedState23637,
227
- altairCachedState23637.slot + 1
228
- ) as CachedBeaconStateAltair;
229
- altairCachedState23638.slot += 1;
281
+ let cachedState23638 = isDefaultVc ? electraCachedState23638 : null;
282
+ if (!cachedState23638) {
283
+ cachedState23638 = processSlots(cachedState23637, cachedState23637.slot + 1) as CachedBeaconStateElectra;
284
+ cachedState23638.slot += 1;
285
+ if (isDefaultVc) electraCachedState23638 = cachedState23638;
230
286
  }
231
- const resultingState = opts?.goBackOneSlot ? altairCachedState23637 : altairCachedState23638;
287
+ const resultingState = opts?.goBackOneSlot ? cachedState23637 : cachedState23638;
232
288
 
233
289
  return resultingState.clone();
234
290
  }
@@ -237,8 +293,12 @@ export function generatePerfTestCachedStateAltair(opts?: {
237
293
  * This is generated from Medalla state 756416
238
294
  */
239
295
  export function generatePerformanceStateAltair(pubkeysArg?: Uint8Array[]): BeaconStateAltair {
240
- if (!altairState) {
241
- const pubkeys = pubkeysArg || getPubkeys().pubkeys;
296
+ const pubkeys = pubkeysArg || getPubkeys().pubkeys;
297
+ const vc = pubkeys.length;
298
+ const isDefaultVc = vc === numValidators;
299
+ // Only use cached state for default vc to avoid accumulating large states in memory
300
+ let cached = isDefaultVc ? altairState : null;
301
+ if (!cached) {
242
302
  const statePhase0 = buildPerformanceStatePhase0(pubkeys);
243
303
  const state = statePhase0 as BeaconState as BeaconState<ForkName.altair>;
244
304
 
@@ -251,27 +311,81 @@ export function generatePerformanceStateAltair(pubkeysArg?: Uint8Array[]): Beaco
251
311
  state.nextSyncCommittee = state.currentSyncCommittee;
252
312
 
253
313
  // Now the state is fully populated to convert to ViewDU
254
- altairState = ssz.altair.BeaconState.toViewDU(state);
314
+ cached = ssz.altair.BeaconState.toViewDU(state);
255
315
 
256
316
  // Now set correct syncCommittees
257
317
  const epoch = computeEpochAtSlot(state.slot);
258
- const activeValidatorIndices = getActiveValidatorIndices(altairState, epoch);
318
+ const activeValidatorIndices = getActiveValidatorIndices(cached, epoch);
259
319
 
260
- const effectiveBalanceIncrements = getEffectiveBalanceIncrements(altairState);
320
+ const effectiveBalanceIncrements = getEffectiveBalanceIncrements(cached);
261
321
  const {syncCommittee} = getNextSyncCommittee(
262
322
  ForkSeq.altair,
263
- altairState,
323
+ cached,
264
324
  activeValidatorIndices,
265
325
  effectiveBalanceIncrements
266
326
  );
267
327
  state.currentSyncCommittee = syncCommittee;
268
328
  state.nextSyncCommittee = syncCommittee;
269
329
 
270
- altairState = ssz.altair.BeaconState.toViewDU(state);
330
+ cached = ssz.altair.BeaconState.toViewDU(state);
271
331
  // cache roots
272
- altairState.hashTreeRoot();
332
+ cached.hashTreeRoot();
333
+ if (isDefaultVc) altairState = cached;
334
+ }
335
+ return cached.clone();
336
+ }
337
+
338
+ /**
339
+ * This is generated from the same performance state as Altair, upgraded to Electra fields.
340
+ */
341
+ export function generatePerformanceStateElectra(pubkeysArg?: Uint8Array[]): BeaconStateElectra {
342
+ const pubkeys = pubkeysArg || getPubkeys().pubkeys;
343
+ const vc = pubkeys.length;
344
+ const isDefaultVc = vc === numValidators;
345
+ // Only use cached state for default vc to avoid accumulating large states in memory
346
+ let cached = isDefaultVc ? electraState : null;
347
+ if (!cached) {
348
+ const electraConfig = createChainForkConfig({
349
+ ALTAIR_FORK_EPOCH: 0,
350
+ BELLATRIX_FORK_EPOCH: 0,
351
+ CAPELLA_FORK_EPOCH: 0,
352
+ DENEB_FORK_EPOCH: 0,
353
+ ELECTRA_FORK_EPOCH: 0,
354
+ });
355
+ const state = ssz.electra.BeaconState.defaultValue();
356
+
357
+ Object.assign(state, buildPerformanceStatePhase0(pubkeys));
358
+
359
+ state.fork.previousVersion = electraConfig.DENEB_FORK_VERSION;
360
+ state.fork.currentVersion = electraConfig.ELECTRA_FORK_VERSION;
361
+ state.fork.epoch = electraConfig.ELECTRA_FORK_EPOCH;
362
+ state.previousEpochParticipation = newFilledArray(pubkeys.length, 0b111);
363
+ state.currentEpochParticipation = state.previousEpochParticipation;
364
+ state.inactivityScores = Array.from({length: pubkeys.length}, (_, i) => i % 2);
365
+ state.currentSyncCommittee = ssz.altair.SyncCommittee.defaultValue();
366
+ state.nextSyncCommittee = state.currentSyncCommittee;
367
+ state.latestExecutionPayloadHeader = ssz.electra.ExecutionPayloadHeader.defaultValue();
368
+ state.depositRequestsStartIndex = 2023n;
369
+
370
+ cached = ssz.electra.BeaconState.toViewDU(state);
371
+
372
+ const epoch = computeEpochAtSlot(state.slot);
373
+ const activeValidatorIndices = getActiveValidatorIndices(cached, epoch);
374
+ const effectiveBalanceIncrements = getEffectiveBalanceIncrements(cached);
375
+ const {syncCommittee} = getNextSyncCommittee(
376
+ ForkSeq.electra,
377
+ cached,
378
+ activeValidatorIndices,
379
+ effectiveBalanceIncrements
380
+ );
381
+ state.currentSyncCommittee = syncCommittee;
382
+ state.nextSyncCommittee = syncCommittee;
383
+
384
+ cached = ssz.electra.BeaconState.toViewDU(state);
385
+ cached.hashTreeRoot();
386
+ if (isDefaultVc) electraState = cached;
273
387
  }
274
- return altairState.clone();
388
+ return cached.clone();
275
389
  }
276
390
 
277
391
  /**
@@ -427,3 +541,25 @@ export function generateTestCachedBeaconStateOnlyValidators({
427
541
  {skipSyncPubkeys: true}
428
542
  );
429
543
  }
544
+
545
+ /**
546
+ * Release all cached perf states to free memory.
547
+ * Only call this before memory-intensive benchmarks that need to allocate
548
+ * very large states (e.g. loadState with 1.5M validators) where the
549
+ * accumulated singletons would cause OOM.
550
+ *
551
+ * WARNING: Do NOT call this in epoch/block benchmark afterAll blocks —
552
+ * destroying singletons mid-suite causes GC storms that corrupt measurements.
553
+ */
554
+ export function clearPerfStateCache(): void {
555
+ phase0State = null;
556
+ phase0CachedState23637 = null;
557
+ phase0CachedState23638 = null;
558
+ phase0SignedBlock = null;
559
+ altairState = null;
560
+ altairCachedState23637 = null;
561
+ altairCachedState23638 = null;
562
+ electraState = null;
563
+ electraCachedState23637 = null;
564
+ electraCachedState23638 = null;
565
+ }
@@ -1,6 +1,5 @@
1
1
  import {Epoch, Root, Slot, phase0} from "@lodestar/types";
2
- import {CachedBeaconStateAllForks} from "../types.js";
3
- import {getBlockRoot, getBlockRootAtSlot} from "./blockRoot.js";
2
+ import {IBeaconStateView} from "../stateView/interface.js";
4
3
 
5
4
  /**
6
5
  * Cache to prevent accessing the state tree to fetch block roots repeteadly.
@@ -12,7 +11,7 @@ export class RootCache {
12
11
  private readonly blockRootEpochCache = new Map<Epoch, Root>();
13
12
  private readonly blockRootSlotCache = new Map<Slot, Root>();
14
13
 
15
- constructor(private readonly state: CachedBeaconStateAllForks) {
14
+ constructor(private readonly state: IBeaconStateView) {
16
15
  this.currentJustifiedCheckpoint = state.currentJustifiedCheckpoint;
17
16
  this.previousJustifiedCheckpoint = state.previousJustifiedCheckpoint;
18
17
  }
@@ -20,7 +19,7 @@ export class RootCache {
20
19
  getBlockRoot(epoch: Epoch): Root {
21
20
  let root = this.blockRootEpochCache.get(epoch);
22
21
  if (!root) {
23
- root = getBlockRoot(this.state, epoch);
22
+ root = this.state.getBlockRootAtEpoch(epoch);
24
23
  this.blockRootEpochCache.set(epoch, root);
25
24
  }
26
25
  return root;
@@ -29,7 +28,7 @@ export class RootCache {
29
28
  getBlockRootAtSlot(slot: Slot): Root {
30
29
  let root = this.blockRootSlotCache.get(slot);
31
30
  if (!root) {
32
- root = getBlockRootAtSlot(this.state, slot);
31
+ root = this.state.getBlockRootAtSlot(slot);
33
32
  this.blockRootSlotCache.set(slot, root);
34
33
  }
35
34
  return root;
@@ -11,6 +11,7 @@ import {
11
11
  } from "@lodestar/types";
12
12
  import {LodestarError} from "@lodestar/utils";
13
13
  import {CachedBeaconStateAllForks} from "../cache/stateCache.js";
14
+ import {IBeaconStateView} from "../stateView/interface.js";
14
15
  import {getBlockRootAtSlot} from "./blockRoot.js";
15
16
  import {computeStartSlotAtEpoch} from "./epoch.js";
16
17
  import {EpochShuffling} from "./epochShuffling.js";
@@ -22,21 +23,21 @@ import {EpochShuffling} from "./epochShuffling.js";
22
23
  * Returns `null` on the one-off scenario where the genesis block decides its own shuffling.
23
24
  * It should be set to the latest block applied to this `state` or the genesis block root.
24
25
  */
25
- export function proposerShufflingDecisionRoot(fork: ForkName, state: CachedBeaconStateAllForks): Root | null {
26
+ export function proposerShufflingDecisionRoot(fork: ForkName, state: IBeaconStateView): Root | null {
26
27
  const decisionSlot = proposerShufflingDecisionSlot(fork, state);
27
28
  if (state.slot === decisionSlot) {
28
29
  return null;
29
30
  }
30
- return getBlockRootAtSlot(state, decisionSlot);
31
+ return state.getBlockRootAtSlot(decisionSlot);
31
32
  }
32
33
 
33
34
  /**
34
35
  * Returns the slot at which the proposer shuffling was decided. The block root at this slot
35
36
  * can be used to key the proposer shuffling for the current epoch.
36
37
  */
37
- function proposerShufflingDecisionSlot(fork: ForkName, state: CachedBeaconStateAllForks): Slot {
38
+ function proposerShufflingDecisionSlot(fork: ForkName, state: IBeaconStateView): Slot {
38
39
  // After fulu, the decision slot is in previous epoch due to deterministic proposer lookahead
39
- const epoch = isForkPostFulu(fork) ? state.epochCtx.epoch - 1 : state.epochCtx.epoch;
40
+ const epoch = isForkPostFulu(fork) ? state.epoch - 1 : state.epoch;
40
41
  const startSlot = computeStartSlotAtEpoch(epoch);
41
42
  return Math.max(startSlot - 1, 0);
42
43
  }