@lodestar/state-transition 1.42.0 → 1.43.0-dev.05a33e512f

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 (132) hide show
  1. package/lib/block/index.d.ts +2 -2
  2. package/lib/block/index.d.ts.map +1 -1
  3. package/lib/block/index.js +11 -4
  4. package/lib/block/index.js.map +1 -1
  5. package/lib/block/processConsolidationRequest.d.ts.map +1 -1
  6. package/lib/block/processConsolidationRequest.js +2 -1
  7. package/lib/block/processConsolidationRequest.js.map +1 -1
  8. package/lib/block/processDepositRequest.d.ts +11 -2
  9. package/lib/block/processDepositRequest.d.ts.map +1 -1
  10. package/lib/block/processDepositRequest.js +34 -4
  11. package/lib/block/processDepositRequest.js.map +1 -1
  12. package/lib/block/processParentExecutionPayload.d.ts +20 -0
  13. package/lib/block/processParentExecutionPayload.d.ts.map +1 -0
  14. package/lib/block/processParentExecutionPayload.js +100 -0
  15. package/lib/block/processParentExecutionPayload.js.map +1 -0
  16. package/lib/block/processWithdrawals.d.ts.map +1 -1
  17. package/lib/block/processWithdrawals.js +10 -4
  18. package/lib/block/processWithdrawals.js.map +1 -1
  19. package/lib/cache/epochCache.d.ts +3 -1
  20. package/lib/cache/epochCache.d.ts.map +1 -1
  21. package/lib/cache/epochCache.js +33 -15
  22. package/lib/cache/epochCache.js.map +1 -1
  23. package/lib/cache/epochTransitionCache.d.ts +5 -0
  24. package/lib/cache/epochTransitionCache.d.ts.map +1 -1
  25. package/lib/cache/epochTransitionCache.js +1 -0
  26. package/lib/cache/epochTransitionCache.js.map +1 -1
  27. package/lib/epoch/index.d.ts +3 -1
  28. package/lib/epoch/index.d.ts.map +1 -1
  29. package/lib/epoch/index.js +8 -1
  30. package/lib/epoch/index.js.map +1 -1
  31. package/lib/epoch/processPendingDeposits.d.ts.map +1 -1
  32. package/lib/epoch/processPendingDeposits.js +4 -2
  33. package/lib/epoch/processPendingDeposits.js.map +1 -1
  34. package/lib/epoch/processPtcWindow.d.ts +11 -0
  35. package/lib/epoch/processPtcWindow.d.ts.map +1 -0
  36. package/lib/epoch/processPtcWindow.js +28 -0
  37. package/lib/epoch/processPtcWindow.js.map +1 -0
  38. package/lib/index.d.ts +4 -2
  39. package/lib/index.d.ts.map +1 -1
  40. package/lib/index.js +4 -1
  41. package/lib/index.js.map +1 -1
  42. package/lib/signatureSets/executionPayloadEnvelope.d.ts.map +1 -1
  43. package/lib/signatureSets/executionPayloadEnvelope.js +5 -1
  44. package/lib/signatureSets/executionPayloadEnvelope.js.map +1 -1
  45. package/lib/signatureSets/index.d.ts +1 -0
  46. package/lib/signatureSets/index.d.ts.map +1 -1
  47. package/lib/signatureSets/index.js +1 -0
  48. package/lib/signatureSets/index.js.map +1 -1
  49. package/lib/signatureSets/proposerPreferences.d.ts +4 -0
  50. package/lib/signatureSets/proposerPreferences.d.ts.map +1 -0
  51. package/lib/signatureSets/proposerPreferences.js +8 -0
  52. package/lib/signatureSets/proposerPreferences.js.map +1 -0
  53. package/lib/signatureSets/voluntaryExits.d.ts +2 -2
  54. package/lib/signatureSets/voluntaryExits.d.ts.map +1 -1
  55. package/lib/signatureSets/voluntaryExits.js +4 -0
  56. package/lib/signatureSets/voluntaryExits.js.map +1 -1
  57. package/lib/slot/upgradeStateToElectra.d.ts.map +1 -1
  58. package/lib/slot/upgradeStateToElectra.js +2 -2
  59. package/lib/slot/upgradeStateToElectra.js.map +1 -1
  60. package/lib/slot/upgradeStateToGloas.d.ts.map +1 -1
  61. package/lib/slot/upgradeStateToGloas.js +3 -1
  62. package/lib/slot/upgradeStateToGloas.js.map +1 -1
  63. package/lib/stateTransition.d.ts +1 -2
  64. package/lib/stateTransition.d.ts.map +1 -1
  65. package/lib/stateTransition.js +1 -2
  66. package/lib/stateTransition.js.map +1 -1
  67. package/lib/stateView/beaconStateView.d.ts +18 -12
  68. package/lib/stateView/beaconStateView.d.ts.map +1 -1
  69. package/lib/stateView/beaconStateView.js +69 -42
  70. package/lib/stateView/beaconStateView.js.map +1 -1
  71. package/lib/stateView/interface.d.ts +111 -54
  72. package/lib/stateView/interface.d.ts.map +1 -1
  73. package/lib/stateView/interface.js +22 -1
  74. package/lib/stateView/interface.js.map +1 -1
  75. package/lib/util/attestation.d.ts +12 -1
  76. package/lib/util/attestation.d.ts.map +1 -1
  77. package/lib/util/attestation.js +23 -8
  78. package/lib/util/attestation.js.map +1 -1
  79. package/lib/util/computeAnchorCheckpoint.d.ts +1 -1
  80. package/lib/util/computeAnchorCheckpoint.d.ts.map +1 -1
  81. package/lib/util/computeAnchorCheckpoint.js +6 -19
  82. package/lib/util/computeAnchorCheckpoint.js.map +1 -1
  83. package/lib/util/epoch.d.ts.map +1 -1
  84. package/lib/util/epoch.js +6 -4
  85. package/lib/util/epoch.js.map +1 -1
  86. package/lib/util/execution.d.ts +4 -2
  87. package/lib/util/execution.d.ts.map +1 -1
  88. package/lib/util/execution.js +7 -0
  89. package/lib/util/execution.js.map +1 -1
  90. package/lib/util/gloas.d.ts +7 -2
  91. package/lib/util/gloas.d.ts.map +1 -1
  92. package/lib/util/gloas.js +25 -3
  93. package/lib/util/gloas.js.map +1 -1
  94. package/lib/util/loadState/loadState.js +4 -4
  95. package/lib/util/loadState/loadState.js.map +1 -1
  96. package/lib/util/validator.d.ts +14 -2
  97. package/lib/util/validator.d.ts.map +1 -1
  98. package/lib/util/validator.js +24 -2
  99. package/lib/util/validator.js.map +1 -1
  100. package/package.json +8 -8
  101. package/src/block/index.ts +12 -4
  102. package/src/block/processConsolidationRequest.ts +2 -1
  103. package/src/block/processDepositRequest.ts +50 -5
  104. package/src/block/processParentExecutionPayload.ts +116 -0
  105. package/src/block/processWithdrawals.ts +12 -4
  106. package/src/cache/epochCache.ts +35 -33
  107. package/src/cache/epochTransitionCache.ts +7 -0
  108. package/src/epoch/index.ts +9 -0
  109. package/src/epoch/processPendingDeposits.ts +5 -2
  110. package/src/epoch/processPtcWindow.ts +38 -0
  111. package/src/index.ts +20 -2
  112. package/src/signatureSets/executionPayloadEnvelope.ts +6 -2
  113. package/src/signatureSets/index.ts +1 -0
  114. package/src/signatureSets/proposerPreferences.ts +12 -0
  115. package/src/signatureSets/voluntaryExits.ts +5 -2
  116. package/src/slot/upgradeStateToElectra.ts +4 -2
  117. package/src/slot/upgradeStateToGloas.ts +5 -1
  118. package/src/stateTransition.ts +1 -2
  119. package/src/stateView/beaconStateView.ts +91 -56
  120. package/src/stateView/interface.ts +163 -79
  121. package/src/util/attestation.ts +37 -8
  122. package/src/util/computeAnchorCheckpoint.ts +6 -19
  123. package/src/util/epoch.ts +13 -4
  124. package/src/util/execution.ts +11 -1
  125. package/src/util/gloas.ts +46 -3
  126. package/src/util/loadState/loadState.ts +4 -4
  127. package/src/util/validator.ts +42 -2
  128. package/lib/block/processExecutionPayloadEnvelope.d.ts +0 -9
  129. package/lib/block/processExecutionPayloadEnvelope.d.ts.map +0 -1
  130. package/lib/block/processExecutionPayloadEnvelope.js +0 -102
  131. package/lib/block/processExecutionPayloadEnvelope.js.map +0 -1
  132. package/src/block/processExecutionPayloadEnvelope.ts +0 -170
@@ -1,5 +1,13 @@
1
- import {MIN_ATTESTATION_INCLUSION_DELAY, SLOTS_PER_EPOCH} from "@lodestar/params";
2
- import {AttesterSlashing, Slot, ValidatorIndex, phase0, ssz} from "@lodestar/types";
1
+ import {ForkSeq, MIN_ATTESTATION_INCLUSION_DELAY, SLOTS_PER_EPOCH} from "@lodestar/params";
2
+ import {
3
+ AttesterSlashing,
4
+ IndexedAttestation,
5
+ IndexedAttestationBigint,
6
+ Slot,
7
+ ValidatorIndex,
8
+ phase0,
9
+ ssz,
10
+ } from "@lodestar/types";
3
11
 
4
12
  /**
5
13
  * Check if [[data1]] and [[data2]] are slashable according to Casper FFG rules.
@@ -22,15 +30,36 @@ export function isValidAttestationSlot(attestationSlot: Slot, currentSlot: Slot)
22
30
  );
23
31
  }
24
32
 
25
- export function getAttesterSlashableIndices(attesterSlashing: AttesterSlashing): ValidatorIndex[] {
33
+ /**
34
+ * Compute the intersection of two sorted validator index lists.
35
+ * Both inputs must be sorted in ascending order (per spec).
36
+ */
37
+ export function getIntersectingIndices(indices1: ValidatorIndex[], indices2: ValidatorIndex[]): ValidatorIndex[] {
26
38
  const indices: ValidatorIndex[] = [];
27
- const attSet1 = new Set(attesterSlashing.attestation1.attestingIndices);
28
- const attArr2 = attesterSlashing.attestation2.attestingIndices;
29
- for (let i = 0, len = attArr2.length; i < len; i++) {
30
- const index = attArr2[i];
31
- if (attSet1.has(index)) {
39
+ const alreadyPresent = new Set(indices1);
40
+ for (let i = 0, len = indices2.length; i < len; i++) {
41
+ const index = indices2[i];
42
+ if (alreadyPresent.has(index)) {
32
43
  indices.push(index);
33
44
  }
34
45
  }
35
46
  return indices;
36
47
  }
48
+
49
+ export function getAttesterSlashableIndices(attesterSlashing: AttesterSlashing): ValidatorIndex[] {
50
+ return getIntersectingIndices(
51
+ attesterSlashing.attestation1.attestingIndices,
52
+ attesterSlashing.attestation2.attestingIndices
53
+ );
54
+ }
55
+
56
+ /**
57
+ * Convert IndexedAttestation to IndexedAttestationBigint via SSZ roundtrip.
58
+ * Both types share the same binary layout — only the JS numeric representation differs.
59
+ */
60
+ export function toIndexedAttestationBigint(att: IndexedAttestation, fork: ForkSeq): IndexedAttestationBigint {
61
+ const sszType = fork >= ForkSeq.electra ? ssz.electra.IndexedAttestation : ssz.phase0.IndexedAttestation;
62
+ const sszTypeBigint =
63
+ fork >= ForkSeq.electra ? ssz.electra.IndexedAttestationBigint : ssz.phase0.IndexedAttestationBigint;
64
+ return sszTypeBigint.deserialize(sszType.serialize(att));
65
+ }
@@ -1,34 +1,21 @@
1
1
  import {ChainForkConfig} from "@lodestar/config";
2
- import {GENESIS_SLOT, ZERO_HASH} from "@lodestar/params";
2
+ import {ZERO_HASH} from "@lodestar/params";
3
3
  import {phase0, ssz} from "@lodestar/types";
4
4
  import {BeaconStateAllForks} from "../types.js";
5
- import {blockToHeader} from "./blockRoot.js";
6
5
  import {computeCheckpointEpochAtStateSlot} from "./epoch.js";
7
6
 
8
7
  export function computeAnchorCheckpoint(
9
- config: ChainForkConfig,
8
+ _config: ChainForkConfig,
10
9
  anchorState: BeaconStateAllForks
11
10
  ): {checkpoint: phase0.Checkpoint; blockHeader: phase0.BeaconBlockHeader} {
12
- let blockHeader: phase0.BeaconBlockHeader;
13
- let root: Uint8Array;
14
- const blockTypes = config.getForkTypes(anchorState.latestBlockHeader.slot);
15
-
16
- if (anchorState.latestBlockHeader.slot === GENESIS_SLOT) {
17
- const block = blockTypes.BeaconBlock.defaultValue();
18
- block.stateRoot = anchorState.hashTreeRoot();
19
- blockHeader = blockToHeader(config, block);
20
- root = ssz.phase0.BeaconBlockHeader.hashTreeRoot(blockHeader);
21
- } else {
22
- blockHeader = ssz.phase0.BeaconBlockHeader.clone(anchorState.latestBlockHeader);
23
- if (ssz.Root.equals(blockHeader.stateRoot, ZERO_HASH)) {
24
- blockHeader.stateRoot = anchorState.hashTreeRoot();
25
- }
26
- root = ssz.phase0.BeaconBlockHeader.hashTreeRoot(blockHeader);
11
+ const blockHeader = ssz.phase0.BeaconBlockHeader.clone(anchorState.latestBlockHeader);
12
+ if (ssz.Root.equals(blockHeader.stateRoot, ZERO_HASH)) {
13
+ blockHeader.stateRoot = anchorState.hashTreeRoot();
27
14
  }
28
15
 
29
16
  return {
30
17
  checkpoint: {
31
- root,
18
+ root: ssz.phase0.BeaconBlockHeader.hashTreeRoot(blockHeader),
32
19
  // the checkpoint epoch = computeEpochAtSlot(anchorState.slot) + 1 if slot is not at epoch boundary
33
20
  // this is similar to a process_slots() call
34
21
  epoch: computeCheckpointEpochAtStateSlot(anchorState.slot),
package/src/util/epoch.ts CHANGED
@@ -1,7 +1,13 @@
1
- import {EPOCHS_PER_SYNC_COMMITTEE_PERIOD, GENESIS_EPOCH, MAX_SEED_LOOKAHEAD, SLOTS_PER_EPOCH} from "@lodestar/params";
1
+ import {
2
+ EPOCHS_PER_SYNC_COMMITTEE_PERIOD,
3
+ ForkSeq,
4
+ GENESIS_EPOCH,
5
+ MAX_SEED_LOOKAHEAD,
6
+ SLOTS_PER_EPOCH,
7
+ } from "@lodestar/params";
2
8
  import {BeaconState, Epoch, Gwei, Slot, SyncPeriod} from "@lodestar/types";
3
9
  import {CachedBeaconStateElectra, CachedBeaconStateGloas} from "../types.js";
4
- import {getActivationExitChurnLimit, getConsolidationChurnLimit} from "./validator.js";
10
+ import {getActivationExitChurnLimit, getConsolidationChurnLimit, getExitChurnLimit} from "./validator.js";
5
11
 
6
12
  /**
7
13
  * Return the epoch number at the given slot.
@@ -45,8 +51,10 @@ export function computeExitEpochAndUpdateChurn(
45
51
  state: CachedBeaconStateElectra | CachedBeaconStateGloas,
46
52
  exitBalance: Gwei
47
53
  ): number {
54
+ const fork = state.config.getForkSeq(state.slot);
48
55
  let earliestExitEpoch = Math.max(state.earliestExitEpoch, computeActivationExitEpoch(state.epochCtx.epoch));
49
- const perEpochChurn = getActivationExitChurnLimit(state.epochCtx);
56
+ const perEpochChurn =
57
+ fork >= ForkSeq.gloas ? getExitChurnLimit(state.epochCtx) : getActivationExitChurnLimit(state.epochCtx);
50
58
 
51
59
  // New epoch for exits.
52
60
  let exitBalanceToConsume =
@@ -71,11 +79,12 @@ export function computeConsolidationEpochAndUpdateChurn(
71
79
  state: CachedBeaconStateElectra | CachedBeaconStateGloas,
72
80
  consolidationBalance: Gwei
73
81
  ): number {
82
+ const fork = state.config.getForkSeq(state.slot);
74
83
  let earliestConsolidationEpoch = Math.max(
75
84
  state.earliestConsolidationEpoch,
76
85
  computeActivationExitEpoch(state.epochCtx.epoch)
77
86
  );
78
- const perEpochConsolidationChurn = getConsolidationChurnLimit(state.epochCtx);
87
+ const perEpochConsolidationChurn = getConsolidationChurnLimit(fork, state.epochCtx);
79
88
 
80
89
  // New epoch for consolidations
81
90
  let consolidationBalanceToConsume =
@@ -18,6 +18,7 @@ import {
18
18
  BeaconStateBellatrix,
19
19
  BeaconStateCapella,
20
20
  BeaconStateExecutions,
21
+ BeaconStateGloas,
21
22
  CachedBeaconStateAllForks,
22
23
  CachedBeaconStateExecutions,
23
24
  } from "../types.js";
@@ -46,7 +47,11 @@ export function isExecutionEnabled(state: BeaconStateExecutions, block: BeaconBl
46
47
  * Merge is complete when the state includes execution layer data:
47
48
  * state.latestExecutionPayloadHeader NOT EMPTY or state is post-capella
48
49
  */
49
- export function isMergeTransitionComplete(state: BeaconStateExecutions): boolean {
50
+ export function isMergeTransitionComplete(state: BeaconStateExecutions | BeaconStateGloas): boolean {
51
+ if (isGloasStateType(state)) {
52
+ return true;
53
+ }
54
+
50
55
  if (isCapellaStateType(state)) {
51
56
  // All networks have completed the merge transition before capella
52
57
  return true;
@@ -71,6 +76,11 @@ export function isCapellaStateType(state: BeaconStateAllForks): state is BeaconS
71
76
  );
72
77
  }
73
78
 
79
+ /** Type guard for gloas.BeaconState */
80
+ export function isGloasStateType(state: BeaconStateAllForks): state is BeaconStateGloas {
81
+ return (state as BeaconStateGloas).latestBlockHash !== undefined;
82
+ }
83
+
74
84
  /** Type guard for bellatrix.CachedBeaconState */
75
85
  export function isExecutionCachedStateType(state: CachedBeaconStateAllForks): state is CachedBeaconStateExecutions {
76
86
  return (state as CachedBeaconStateExecutions).latestExecutionPayloadHeader !== undefined;
package/src/util/gloas.ts CHANGED
@@ -6,15 +6,20 @@ import {
6
6
  EFFECTIVE_BALANCE_INCREMENT,
7
7
  FAR_FUTURE_EPOCH,
8
8
  MIN_DEPOSIT_AMOUNT,
9
+ MIN_SEED_LOOKAHEAD,
10
+ PTC_SIZE,
9
11
  SLOTS_PER_EPOCH,
10
12
  } from "@lodestar/params";
11
13
  import {BuilderIndex, Epoch, ValidatorIndex, gloas} from "@lodestar/types";
12
14
  import {AttestationData} from "@lodestar/types/phase0";
13
15
  import {byteArrayEquals} from "@lodestar/utils";
14
- import {CachedBeaconStateGloas} from "../types.js";
16
+ import {CachedBeaconStateFulu, CachedBeaconStateGloas} from "../types.js";
15
17
  import {getBlockRootAtSlot} from "./blockRoot.js";
16
18
  import {computeEpochAtSlot} from "./epoch.js";
19
+ import {computeEpochShuffling} from "./epochShuffling.js";
17
20
  import {RootCache} from "./rootCache.js";
21
+ import {computePayloadTimelinessCommitteesForEpoch} from "./seed.js";
22
+ import {getActiveValidatorIndices} from "./validator.js";
18
23
 
19
24
  export function isBuilderWithdrawalCredential(withdrawalCredentials: Uint8Array): boolean {
20
25
  return withdrawalCredentials[0] === BUILDER_WITHDRAWAL_PREFIX;
@@ -167,6 +172,44 @@ export function isAttestationSameSlotRootCache(rootCache: RootCache, data: Attes
167
172
  return isMatchingBlockRoot && isCurrentBlockRoot;
168
173
  }
169
174
 
170
- export function isParentBlockFull(state: CachedBeaconStateGloas): boolean {
171
- return byteArrayEquals(state.latestExecutionPayloadBid.blockHash, state.latestBlockHash);
175
+ export function initializePtcWindow(state: CachedBeaconStateFulu): Uint32Array[] {
176
+ const ptcWindow: Uint32Array[] = Array.from({length: SLOTS_PER_EPOCH}, () => new Uint32Array(PTC_SIZE));
177
+ const currentEpoch = state.epochCtx.epoch;
178
+
179
+ for (let epochOffset = 0; epochOffset <= MIN_SEED_LOOKAHEAD; epochOffset++) {
180
+ const epoch = currentEpoch + epochOffset;
181
+ const shuffling =
182
+ state.epochCtx.getShufflingAtEpochOrNull(epoch) ??
183
+ computeEpochShuffling(state, getActiveValidatorIndices(state, epoch), epoch);
184
+
185
+ ptcWindow.push(
186
+ ...computePayloadTimelinessCommitteesForEpoch(
187
+ state,
188
+ epoch,
189
+ shuffling.committees,
190
+ state.epochCtx.effectiveBalanceIncrements
191
+ )
192
+ );
193
+ }
194
+
195
+ return ptcWindow;
196
+ }
197
+
198
+ export function getPtcWindowEpochCacheData(state: CachedBeaconStateGloas): {
199
+ previousPayloadTimelinessCommittees: Uint32Array[];
200
+ payloadTimelinessCommittees: Uint32Array[];
201
+ nextPayloadTimelinessCommittees: Uint32Array[];
202
+ } {
203
+ const toUint32Arrays = (views: ReturnType<typeof state.ptcWindow.getReadonlyByRange>) =>
204
+ views.map((v) => Uint32Array.from(v.getAll()));
205
+
206
+ const previousPtcWindow = state.ptcWindow.getReadonlyByRange(0, SLOTS_PER_EPOCH);
207
+ const currentPtcWindow = state.ptcWindow.getReadonlyByRange(SLOTS_PER_EPOCH, SLOTS_PER_EPOCH);
208
+ const nextPtcWindow = state.ptcWindow.getReadonlyByRange(2 * SLOTS_PER_EPOCH, SLOTS_PER_EPOCH);
209
+
210
+ return {
211
+ previousPayloadTimelinessCommittees: toUint32Arrays(previousPtcWindow),
212
+ payloadTimelinessCommittees: toUint32Arrays(currentPtcWindow),
213
+ nextPayloadTimelinessCommittees: toUint32Arrays(nextPtcWindow),
214
+ };
172
215
  }
@@ -110,8 +110,8 @@ function loadInactivityScores(
110
110
  seedState: BeaconStateAltair,
111
111
  inactivityScoresBytes: Uint8Array
112
112
  ): void {
113
- // migratedState starts with the same inactivityScores to seed state
114
- migratedState.inactivityScores = seedState.inactivityScores.clone();
113
+ // true = do not transfer cache
114
+ migratedState.inactivityScores = seedState.inactivityScores.clone(true);
115
115
  const oldValidator = migratedState.inactivityScores.length;
116
116
  // UintNum64 = 8 bytes
117
117
  const newValidator = inactivityScoresBytes.length / 8;
@@ -187,8 +187,8 @@ function loadValidators(
187
187
  const newValidatorCount = Math.floor(newValidatorsBytes.length / VALIDATOR_BYTES_SIZE);
188
188
  const isMoreValidator = newValidatorCount >= seedValidatorCount;
189
189
  const minValidatorCount = Math.min(seedValidatorCount, newValidatorCount);
190
- // migrated state starts with the same validators to seed state
191
- migratedState.validators = seedState.validators.clone();
190
+ // true = do not transfer cache
191
+ migratedState.validators = seedState.validators.clone(true);
192
192
  // 80% of validators serialization time comes from memory allocation
193
193
  // seedStateValidatorsBytes is an optimization at beacon-node side to avoid memory allocation here
194
194
  const seedValidatorsBytes = seedStateValidatorsBytes ?? seedState.validators.serialize();
@@ -44,7 +44,12 @@ export function getActiveValidatorIndices(state: BeaconStateAllForks, epoch: Epo
44
44
  return new Uint32Array(indices);
45
45
  }
46
46
 
47
- export function getActivationChurnLimit(config: ChainForkConfig, fork: ForkSeq, activeValidatorCount: number): number {
47
+ // Deneb fork upgrade only
48
+ export function getValidatorActivationChurnLimit(
49
+ config: ChainForkConfig,
50
+ fork: ForkSeq,
51
+ activeValidatorCount: number
52
+ ): number {
48
53
  if (fork >= ForkSeq.deneb) {
49
54
  return Math.min(config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT, getChurnLimit(config, activeValidatorCount));
50
55
  }
@@ -84,7 +89,42 @@ export function getActivationExitChurnLimit(epochCtx: EpochCache): number {
84
89
  return Math.min(epochCtx.config.MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT, getBalanceChurnLimitFromCache(epochCtx));
85
90
  }
86
91
 
87
- export function getConsolidationChurnLimit(epochCtx: EpochCache): number {
92
+ /**
93
+ * https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.6/specs/gloas/beacon-chain.md#new-get_activation_churn_limit
94
+ */
95
+ export function getActivationChurnLimit(epochCtx: EpochCache): number {
96
+ const churn = getBalanceChurnLimit(
97
+ epochCtx.totalActiveBalanceIncrements,
98
+ epochCtx.config.CHURN_LIMIT_QUOTIENT_GLOAS,
99
+ epochCtx.config.MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA
100
+ );
101
+ return Math.min(epochCtx.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT_GLOAS, churn);
102
+ }
103
+
104
+ /**
105
+ * https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.6/specs/gloas/beacon-chain.md#new-get_exit_churn_limit
106
+ */
107
+ export function getExitChurnLimit(epochCtx: EpochCache): number {
108
+ return getBalanceChurnLimit(
109
+ epochCtx.totalActiveBalanceIncrements,
110
+ epochCtx.config.CHURN_LIMIT_QUOTIENT_GLOAS,
111
+ epochCtx.config.MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA
112
+ );
113
+ }
114
+
115
+ /**
116
+ * Spec (electra): get_consolidation_churn_limit (uses combined balance churn minus activation+exit churn)
117
+ * Spec (gloas): get_consolidation_churn_limit (independent quotient, no MIN floor)
118
+ */
119
+ export function getConsolidationChurnLimit(fork: ForkSeq, epochCtx: EpochCache): number {
120
+ if (fork >= ForkSeq.gloas) {
121
+ // No MIN floor — pass 0 so getBalanceChurnLimit's max(churn, min) is a no-op.
122
+ return getBalanceChurnLimit(
123
+ epochCtx.totalActiveBalanceIncrements,
124
+ epochCtx.config.CONSOLIDATION_CHURN_LIMIT_QUOTIENT,
125
+ 0
126
+ );
127
+ }
88
128
  return getBalanceChurnLimitFromCache(epochCtx) - getActivationExitChurnLimit(epochCtx);
89
129
  }
90
130
 
@@ -1,9 +0,0 @@
1
- import { gloas } from "@lodestar/types";
2
- import { CachedBeaconStateGloas } from "../types.js";
3
- export type ProcessExecutionPayloadEnvelopeOpts = {
4
- verifySignature?: boolean;
5
- verifyStateRoot?: boolean;
6
- dontTransferCache?: boolean;
7
- };
8
- export declare function processExecutionPayloadEnvelope(state: CachedBeaconStateGloas, signedEnvelope: gloas.SignedExecutionPayloadEnvelope, opts?: ProcessExecutionPayloadEnvelopeOpts): CachedBeaconStateGloas;
9
- //# sourceMappingURL=processExecutionPayloadEnvelope.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"processExecutionPayloadEnvelope.d.ts","sourceRoot":"","sources":["../../src/block/processExecutionPayloadEnvelope.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,KAAK,EAAM,MAAM,iBAAiB,CAAC;AAI3C,OAAO,EAAC,sBAAsB,EAAC,MAAM,aAAa,CAAC;AAOnD,MAAM,MAAM,mCAAmC,GAAG;IAChD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAKF,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,sBAAsB,EAC7B,cAAc,EAAE,KAAK,CAAC,8BAA8B,EACpD,IAAI,CAAC,EAAE,mCAAmC,GACzC,sBAAsB,CAqDxB"}
@@ -1,102 +0,0 @@
1
- import { SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT } from "@lodestar/params";
2
- import { ssz } from "@lodestar/types";
3
- import { byteArrayEquals, toHex, toRootHex } from "@lodestar/utils";
4
- import { getExecutionPayloadEnvelopeSignatureSet } from "../signatureSets/executionPayloadEnvelope.js";
5
- import { BeaconStateView } from "../stateView/beaconStateView.js";
6
- import { computeTimeAtSlot } from "../util/index.js";
7
- import { verifySignatureSet } from "../util/signatureSets.js";
8
- import { processConsolidationRequest } from "./processConsolidationRequest.js";
9
- import { processDepositRequest } from "./processDepositRequest.js";
10
- import { processWithdrawalRequest } from "./processWithdrawalRequest.js";
11
- // Unlike other block processing functions which mutate state in-place, this function
12
- // clones the state and returns the post-state, similar to stateTransition().
13
- // This function does not call execution engine to verify payload. Need to call it from other place.
14
- export function processExecutionPayloadEnvelope(state, signedEnvelope, opts) {
15
- const { verifySignature = true, verifyStateRoot = true } = opts ?? {};
16
- const envelope = signedEnvelope.message;
17
- const payload = envelope.payload;
18
- const fork = state.config.getForkSeq(envelope.slot);
19
- if (verifySignature && !verifyExecutionPayloadEnvelopeSignature(state, signedEnvelope)) {
20
- throw Error(`Execution payload envelope has invalid signature builderIndex=${envelope.builderIndex}`);
21
- }
22
- // .clone() before mutating state, similar to stateTransition()
23
- const postState = state.clone(opts?.dontTransferCache);
24
- validateExecutionPayloadEnvelope(postState, envelope);
25
- const requests = envelope.executionRequests;
26
- for (const deposit of requests.deposits) {
27
- processDepositRequest(fork, postState, deposit);
28
- }
29
- for (const withdrawal of requests.withdrawals) {
30
- processWithdrawalRequest(fork, postState, withdrawal);
31
- }
32
- for (const consolidation of requests.consolidations) {
33
- processConsolidationRequest(postState, consolidation);
34
- }
35
- // Queue the builder payment
36
- const paymentIndex = SLOTS_PER_EPOCH + (postState.slot % SLOTS_PER_EPOCH);
37
- const payment = postState.builderPendingPayments.get(paymentIndex).clone();
38
- const amount = payment.withdrawal.amount;
39
- if (amount > 0) {
40
- postState.builderPendingWithdrawals.push(payment.withdrawal);
41
- }
42
- postState.builderPendingPayments.set(paymentIndex, ssz.gloas.BuilderPendingPayment.defaultViewDU());
43
- // Cache the execution payload hash
44
- postState.executionPayloadAvailability.set(postState.slot % SLOTS_PER_HISTORICAL_ROOT, true);
45
- postState.latestBlockHash = payload.blockHash;
46
- postState.commit();
47
- if (verifyStateRoot && !byteArrayEquals(envelope.stateRoot, postState.hashTreeRoot())) {
48
- throw new Error(`Envelope's state root does not match state envelope=${toRootHex(envelope.stateRoot)} state=${toRootHex(postState.hashTreeRoot())}`);
49
- }
50
- return postState;
51
- }
52
- function validateExecutionPayloadEnvelope(state, envelope) {
53
- const payload = envelope.payload;
54
- // Cache latest block header state root
55
- if (byteArrayEquals(state.latestBlockHeader.stateRoot, ssz.Root.defaultValue())) {
56
- const previousStateRoot = state.hashTreeRoot();
57
- state.latestBlockHeader.stateRoot = previousStateRoot;
58
- }
59
- // Verify consistency with the beacon block
60
- if (!byteArrayEquals(envelope.beaconBlockRoot, state.latestBlockHeader.hashTreeRoot())) {
61
- throw new Error(`Envelope's block is not the latest block header envelope=${toRootHex(envelope.beaconBlockRoot)} latestBlockHeader=${toRootHex(state.latestBlockHeader.hashTreeRoot())}`);
62
- }
63
- if (envelope.slot !== state.slot) {
64
- throw new Error(`Slot mismatch between envelope and state envelope=${envelope.slot} state=${state.slot}`);
65
- }
66
- // Verify consistency with the committed bid
67
- const committedBid = state.latestExecutionPayloadBid;
68
- if (envelope.builderIndex !== committedBid.builderIndex) {
69
- throw new Error(`Builder index mismatch between envelope and committed bid envelope=${envelope.builderIndex} committedBid=${committedBid.builderIndex}`);
70
- }
71
- if (!byteArrayEquals(committedBid.prevRandao, payload.prevRandao)) {
72
- throw new Error(`Prev randao mismatch between committed bid and payload committedBid=${toHex(committedBid.prevRandao)} payload=${toHex(payload.prevRandao)}`);
73
- }
74
- // Verify consistency with expected withdrawals
75
- const payloadWithdrawalsRoot = ssz.capella.Withdrawals.hashTreeRoot(payload.withdrawals);
76
- const expectedWithdrawalsRoot = state.payloadExpectedWithdrawals.hashTreeRoot();
77
- if (!byteArrayEquals(payloadWithdrawalsRoot, expectedWithdrawalsRoot)) {
78
- throw new Error(`Withdrawals mismatch between payload and expected withdrawals payload=${toRootHex(payloadWithdrawalsRoot)} expected=${toRootHex(expectedWithdrawalsRoot)}`);
79
- }
80
- // Verify the gas_limit
81
- if (Number(committedBid.gasLimit) !== payload.gasLimit) {
82
- throw new Error(`Gas limit mismatch between envelope's payload and committed bid envelope=${payload.gasLimit} committedBid=${Number(committedBid.gasLimit)}`);
83
- }
84
- // Verify the block hash
85
- if (!byteArrayEquals(committedBid.blockHash, payload.blockHash)) {
86
- throw new Error(`Block hash mismatch between envelope's payload and committed bid envelope=${toRootHex(payload.blockHash)} committedBid=${toRootHex(committedBid.blockHash)}`);
87
- }
88
- // Verify consistency of the parent hash with respect to the previous execution payload
89
- if (!byteArrayEquals(payload.parentHash, state.latestBlockHash)) {
90
- throw new Error(`Parent hash mismatch between envelope's payload and state envelope=${toRootHex(payload.parentHash)} state=${toRootHex(state.latestBlockHash)}`);
91
- }
92
- // Verify timestamp
93
- if (payload.timestamp !== computeTimeAtSlot(state.config, state.slot, state.genesisTime)) {
94
- throw new Error(`Timestamp mismatch between envelope's payload and state envelope=${payload.timestamp} state=${computeTimeAtSlot(state.config, state.slot, state.genesisTime)}`);
95
- }
96
- // Skipped: Verify the execution payload is valid
97
- }
98
- function verifyExecutionPayloadEnvelopeSignature(state, signedEnvelope) {
99
- const signatureSet = getExecutionPayloadEnvelopeSignatureSet(state.config, state.epochCtx.pubkeyCache, new BeaconStateView(state), signedEnvelope, state.latestBlockHeader.proposerIndex);
100
- return verifySignatureSet(signatureSet);
101
- }
102
- //# sourceMappingURL=processExecutionPayloadEnvelope.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"processExecutionPayloadEnvelope.js","sourceRoot":"","sources":["../../src/block/processExecutionPayloadEnvelope.ts"],"names":[],"mappings":"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"}
@@ -1,170 +0,0 @@
1
- import {SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
2
- import {gloas, ssz} from "@lodestar/types";
3
- import {byteArrayEquals, toHex, toRootHex} from "@lodestar/utils";
4
- import {getExecutionPayloadEnvelopeSignatureSet} from "../signatureSets/executionPayloadEnvelope.js";
5
- import {BeaconStateView} from "../stateView/beaconStateView.js";
6
- import {CachedBeaconStateGloas} from "../types.js";
7
- import {computeTimeAtSlot} from "../util/index.js";
8
- import {verifySignatureSet} from "../util/signatureSets.js";
9
- import {processConsolidationRequest} from "./processConsolidationRequest.js";
10
- import {processDepositRequest} from "./processDepositRequest.js";
11
- import {processWithdrawalRequest} from "./processWithdrawalRequest.js";
12
-
13
- export type ProcessExecutionPayloadEnvelopeOpts = {
14
- verifySignature?: boolean;
15
- verifyStateRoot?: boolean;
16
- dontTransferCache?: boolean;
17
- };
18
-
19
- // Unlike other block processing functions which mutate state in-place, this function
20
- // clones the state and returns the post-state, similar to stateTransition().
21
- // This function does not call execution engine to verify payload. Need to call it from other place.
22
- export function processExecutionPayloadEnvelope(
23
- state: CachedBeaconStateGloas,
24
- signedEnvelope: gloas.SignedExecutionPayloadEnvelope,
25
- opts?: ProcessExecutionPayloadEnvelopeOpts
26
- ): CachedBeaconStateGloas {
27
- const {verifySignature = true, verifyStateRoot = true} = opts ?? {};
28
- const envelope = signedEnvelope.message;
29
- const payload = envelope.payload;
30
- const fork = state.config.getForkSeq(envelope.slot);
31
-
32
- if (verifySignature && !verifyExecutionPayloadEnvelopeSignature(state, signedEnvelope)) {
33
- throw Error(`Execution payload envelope has invalid signature builderIndex=${envelope.builderIndex}`);
34
- }
35
-
36
- // .clone() before mutating state, similar to stateTransition()
37
- const postState = state.clone(opts?.dontTransferCache) as CachedBeaconStateGloas;
38
-
39
- validateExecutionPayloadEnvelope(postState, envelope);
40
-
41
- const requests = envelope.executionRequests;
42
-
43
- for (const deposit of requests.deposits) {
44
- processDepositRequest(fork, postState, deposit);
45
- }
46
-
47
- for (const withdrawal of requests.withdrawals) {
48
- processWithdrawalRequest(fork, postState, withdrawal);
49
- }
50
-
51
- for (const consolidation of requests.consolidations) {
52
- processConsolidationRequest(postState, consolidation);
53
- }
54
-
55
- // Queue the builder payment
56
- const paymentIndex = SLOTS_PER_EPOCH + (postState.slot % SLOTS_PER_EPOCH);
57
- const payment = postState.builderPendingPayments.get(paymentIndex).clone();
58
- const amount = payment.withdrawal.amount;
59
-
60
- if (amount > 0) {
61
- postState.builderPendingWithdrawals.push(payment.withdrawal);
62
- }
63
-
64
- postState.builderPendingPayments.set(paymentIndex, ssz.gloas.BuilderPendingPayment.defaultViewDU());
65
-
66
- // Cache the execution payload hash
67
- postState.executionPayloadAvailability.set(postState.slot % SLOTS_PER_HISTORICAL_ROOT, true);
68
- postState.latestBlockHash = payload.blockHash;
69
-
70
- postState.commit();
71
-
72
- if (verifyStateRoot && !byteArrayEquals(envelope.stateRoot, postState.hashTreeRoot())) {
73
- throw new Error(
74
- `Envelope's state root does not match state envelope=${toRootHex(envelope.stateRoot)} state=${toRootHex(postState.hashTreeRoot())}`
75
- );
76
- }
77
-
78
- return postState;
79
- }
80
-
81
- function validateExecutionPayloadEnvelope(
82
- state: CachedBeaconStateGloas,
83
- envelope: gloas.ExecutionPayloadEnvelope
84
- ): void {
85
- const payload = envelope.payload;
86
-
87
- // Cache latest block header state root
88
- if (byteArrayEquals(state.latestBlockHeader.stateRoot, ssz.Root.defaultValue())) {
89
- const previousStateRoot = state.hashTreeRoot();
90
- state.latestBlockHeader.stateRoot = previousStateRoot;
91
- }
92
-
93
- // Verify consistency with the beacon block
94
- if (!byteArrayEquals(envelope.beaconBlockRoot, state.latestBlockHeader.hashTreeRoot())) {
95
- throw new Error(
96
- `Envelope's block is not the latest block header envelope=${toRootHex(envelope.beaconBlockRoot)} latestBlockHeader=${toRootHex(state.latestBlockHeader.hashTreeRoot())}`
97
- );
98
- }
99
-
100
- if (envelope.slot !== state.slot) {
101
- throw new Error(`Slot mismatch between envelope and state envelope=${envelope.slot} state=${state.slot}`);
102
- }
103
-
104
- // Verify consistency with the committed bid
105
- const committedBid = state.latestExecutionPayloadBid;
106
- if (envelope.builderIndex !== committedBid.builderIndex) {
107
- throw new Error(
108
- `Builder index mismatch between envelope and committed bid envelope=${envelope.builderIndex} committedBid=${committedBid.builderIndex}`
109
- );
110
- }
111
-
112
- if (!byteArrayEquals(committedBid.prevRandao, payload.prevRandao)) {
113
- throw new Error(
114
- `Prev randao mismatch between committed bid and payload committedBid=${toHex(committedBid.prevRandao)} payload=${toHex(payload.prevRandao)}`
115
- );
116
- }
117
-
118
- // Verify consistency with expected withdrawals
119
- const payloadWithdrawalsRoot = ssz.capella.Withdrawals.hashTreeRoot(payload.withdrawals);
120
- const expectedWithdrawalsRoot = state.payloadExpectedWithdrawals.hashTreeRoot();
121
- if (!byteArrayEquals(payloadWithdrawalsRoot, expectedWithdrawalsRoot)) {
122
- throw new Error(
123
- `Withdrawals mismatch between payload and expected withdrawals payload=${toRootHex(payloadWithdrawalsRoot)} expected=${toRootHex(expectedWithdrawalsRoot)}`
124
- );
125
- }
126
-
127
- // Verify the gas_limit
128
- if (Number(committedBid.gasLimit) !== payload.gasLimit) {
129
- throw new Error(
130
- `Gas limit mismatch between envelope's payload and committed bid envelope=${payload.gasLimit} committedBid=${Number(committedBid.gasLimit)}`
131
- );
132
- }
133
-
134
- // Verify the block hash
135
- if (!byteArrayEquals(committedBid.blockHash, payload.blockHash)) {
136
- throw new Error(
137
- `Block hash mismatch between envelope's payload and committed bid envelope=${toRootHex(payload.blockHash)} committedBid=${toRootHex(committedBid.blockHash)}`
138
- );
139
- }
140
-
141
- // Verify consistency of the parent hash with respect to the previous execution payload
142
- if (!byteArrayEquals(payload.parentHash, state.latestBlockHash)) {
143
- throw new Error(
144
- `Parent hash mismatch between envelope's payload and state envelope=${toRootHex(payload.parentHash)} state=${toRootHex(state.latestBlockHash)}`
145
- );
146
- }
147
-
148
- // Verify timestamp
149
- if (payload.timestamp !== computeTimeAtSlot(state.config, state.slot, state.genesisTime)) {
150
- throw new Error(
151
- `Timestamp mismatch between envelope's payload and state envelope=${payload.timestamp} state=${computeTimeAtSlot(state.config, state.slot, state.genesisTime)}`
152
- );
153
- }
154
-
155
- // Skipped: Verify the execution payload is valid
156
- }
157
-
158
- function verifyExecutionPayloadEnvelopeSignature(
159
- state: CachedBeaconStateGloas,
160
- signedEnvelope: gloas.SignedExecutionPayloadEnvelope
161
- ): boolean {
162
- const signatureSet = getExecutionPayloadEnvelopeSignatureSet(
163
- state.config,
164
- state.epochCtx.pubkeyCache,
165
- new BeaconStateView(state),
166
- signedEnvelope,
167
- state.latestBlockHeader.proposerIndex
168
- );
169
- return verifySignatureSet(signatureSet);
170
- }