@lodestar/state-transition 1.42.0-dev.7df0e2c8fa → 1.42.0-dev.8300b502a6

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 (53) hide show
  1. package/lib/index.d.ts +4 -2
  2. package/lib/index.d.ts.map +1 -1
  3. package/lib/index.js +4 -1
  4. package/lib/index.js.map +1 -1
  5. package/lib/signatureSets/executionPayloadEnvelope.d.ts.map +1 -1
  6. package/lib/signatureSets/executionPayloadEnvelope.js +4 -0
  7. package/lib/signatureSets/executionPayloadEnvelope.js.map +1 -1
  8. package/lib/signatureSets/voluntaryExits.d.ts +2 -2
  9. package/lib/signatureSets/voluntaryExits.d.ts.map +1 -1
  10. package/lib/signatureSets/voluntaryExits.js +4 -0
  11. package/lib/signatureSets/voluntaryExits.js.map +1 -1
  12. package/lib/stateTransition.d.ts +1 -1
  13. package/lib/stateTransition.d.ts.map +1 -1
  14. package/lib/stateTransition.js +1 -1
  15. package/lib/stateTransition.js.map +1 -1
  16. package/lib/stateView/beaconStateView.d.ts +6 -8
  17. package/lib/stateView/beaconStateView.d.ts.map +1 -1
  18. package/lib/stateView/beaconStateView.js +18 -31
  19. package/lib/stateView/beaconStateView.js.map +1 -1
  20. package/lib/stateView/index.d.ts +1 -0
  21. package/lib/stateView/index.d.ts.map +1 -1
  22. package/lib/stateView/index.js +1 -0
  23. package/lib/stateView/index.js.map +1 -1
  24. package/lib/stateView/interface.d.ts +98 -51
  25. package/lib/stateView/interface.d.ts.map +1 -1
  26. package/lib/stateView/interface.js +22 -1
  27. package/lib/stateView/interface.js.map +1 -1
  28. package/lib/stateView/stateViewFactory.d.ts +40 -0
  29. package/lib/stateView/stateViewFactory.d.ts.map +1 -0
  30. package/lib/stateView/stateViewFactory.js +46 -0
  31. package/lib/stateView/stateViewFactory.js.map +1 -0
  32. package/lib/testUtils/cache.d.ts.map +1 -1
  33. package/lib/testUtils/cache.js +1 -1
  34. package/lib/testUtils/cache.js.map +1 -1
  35. package/lib/testUtils/util.d.ts +13 -1
  36. package/lib/testUtils/util.d.ts.map +1 -1
  37. package/lib/testUtils/util.js +119 -19
  38. package/lib/testUtils/util.js.map +1 -1
  39. package/lib/util/gloas.d.ts +2 -1
  40. package/lib/util/gloas.d.ts.map +1 -1
  41. package/lib/util/gloas.js.map +1 -1
  42. package/package.json +7 -7
  43. package/src/index.ts +20 -2
  44. package/src/signatureSets/executionPayloadEnvelope.ts +5 -1
  45. package/src/signatureSets/voluntaryExits.ts +5 -2
  46. package/src/stateTransition.ts +1 -1
  47. package/src/stateView/beaconStateView.ts +26 -49
  48. package/src/stateView/index.ts +1 -0
  49. package/src/stateView/interface.ts +143 -73
  50. package/src/stateView/stateViewFactory.ts +78 -0
  51. package/src/testUtils/cache.ts +1 -1
  52. package/src/testUtils/util.ts +136 -22
  53. package/src/util/gloas.ts +2 -1
@@ -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
 
@@ -215,8 +160,133 @@ export interface IBeaconStateView {
215
160
  epochTransitionCacheOpts?: EpochTransitionCacheOpts & {dontTransferCache?: boolean},
216
161
  modules?: StateTransitionModules
217
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
+ payloadExpectedWithdrawals: capella.Withdrawal[];
250
+ getBuilder(index: BuilderIndex): gloas.Builder;
251
+ canBuilderCoverBid(builderIndex: BuilderIndex, bidAmount: number): boolean;
252
+ getIndexInPayloadTimelinessCommittee(validatorIndex: ValidatorIndex, slot: Slot): number;
218
253
  processExecutionPayloadEnvelope(
219
254
  signedEnvelope: gloas.SignedExecutionPayloadEnvelope,
220
255
  opts?: ProcessExecutionPayloadEnvelopeOpts
221
256
  ): IBeaconStateView;
222
257
  }
258
+
259
+ /**
260
+ * Type constraint for the concrete BeaconStateView class.
261
+ * Requires all fields from the latest fork interface (IBeaconStateViewGloas) but keeps
262
+ * forkName as ForkName since the class wraps any fork's state.
263
+ * Sub-interfaces retain their narrowed forkName discriminants for caller-side type guards.
264
+ */
265
+ export type IBeaconStateViewLatestFork = Omit<IBeaconStateViewGloas, "forkName"> & {forkName: ForkName};
266
+ export function isStatePostAltair(state: IBeaconStateView): state is IBeaconStateViewAltair {
267
+ return isForkPostAltair(state.forkName);
268
+ }
269
+
270
+ export function isStatePostBellatrix(state: IBeaconStateView): state is IBeaconStateViewBellatrix {
271
+ return isForkPostBellatrix(state.forkName);
272
+ }
273
+
274
+ export function isStatePostCapella(state: IBeaconStateView): state is IBeaconStateViewCapella {
275
+ return isForkPostCapella(state.forkName);
276
+ }
277
+
278
+ export function isStatePostDeneb(state: IBeaconStateView): state is IBeaconStateViewDeneb {
279
+ return isForkPostDeneb(state.forkName);
280
+ }
281
+
282
+ export function isStatePostElectra(state: IBeaconStateView): state is IBeaconStateViewElectra {
283
+ return isForkPostElectra(state.forkName);
284
+ }
285
+
286
+ export function isStatePostFulu(state: IBeaconStateView): state is IBeaconStateViewFulu {
287
+ return isForkPostFulu(state.forkName);
288
+ }
289
+
290
+ export function isStatePostGloas(state: IBeaconStateView): state is IBeaconStateViewGloas {
291
+ return isForkPostGloas(state.forkName);
292
+ }
@@ -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");
@@ -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
  /**
package/src/util/gloas.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  import {BuilderIndex, Epoch, ValidatorIndex, gloas} from "@lodestar/types";
12
12
  import {AttestationData} from "@lodestar/types/phase0";
13
13
  import {byteArrayEquals} from "@lodestar/utils";
14
+ import {IBeaconStateViewGloas} from "../stateView/interface.js";
14
15
  import {CachedBeaconStateGloas} from "../types.js";
15
16
  import {getBlockRootAtSlot} from "./blockRoot.js";
16
17
  import {computeEpochAtSlot} from "./epoch.js";
@@ -167,6 +168,6 @@ export function isAttestationSameSlotRootCache(rootCache: RootCache, data: Attes
167
168
  return isMatchingBlockRoot && isCurrentBlockRoot;
168
169
  }
169
170
 
170
- export function isParentBlockFull(state: CachedBeaconStateGloas): boolean {
171
+ export function isParentBlockFull(state: CachedBeaconStateGloas | IBeaconStateViewGloas): boolean {
171
172
  return byteArrayEquals(state.latestExecutionPayloadBid.blockHash, state.latestBlockHash);
172
173
  }