@lodestar/state-transition 1.43.0-dev.433e692fd9 → 1.43.0-dev.4451fec75a
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.
- package/lib/block/index.d.ts +2 -2
- package/lib/block/index.d.ts.map +1 -1
- package/lib/block/index.js +11 -4
- package/lib/block/index.js.map +1 -1
- package/lib/block/processConsolidationRequest.d.ts.map +1 -1
- package/lib/block/processConsolidationRequest.js +2 -1
- package/lib/block/processConsolidationRequest.js.map +1 -1
- package/lib/block/processParentExecutionPayload.d.ts +20 -0
- package/lib/block/processParentExecutionPayload.d.ts.map +1 -0
- package/lib/block/processParentExecutionPayload.js +100 -0
- package/lib/block/processParentExecutionPayload.js.map +1 -0
- package/lib/block/processWithdrawals.d.ts.map +1 -1
- package/lib/block/processWithdrawals.js +10 -4
- package/lib/block/processWithdrawals.js.map +1 -1
- package/lib/cache/epochCache.d.ts +3 -1
- package/lib/cache/epochCache.d.ts.map +1 -1
- package/lib/cache/epochCache.js +33 -15
- package/lib/cache/epochCache.js.map +1 -1
- package/lib/cache/epochTransitionCache.d.ts +5 -0
- package/lib/cache/epochTransitionCache.d.ts.map +1 -1
- package/lib/cache/epochTransitionCache.js +1 -0
- package/lib/cache/epochTransitionCache.js.map +1 -1
- package/lib/epoch/index.d.ts +3 -1
- package/lib/epoch/index.d.ts.map +1 -1
- package/lib/epoch/index.js +8 -1
- package/lib/epoch/index.js.map +1 -1
- package/lib/epoch/processPendingDeposits.d.ts.map +1 -1
- package/lib/epoch/processPendingDeposits.js +4 -2
- package/lib/epoch/processPendingDeposits.js.map +1 -1
- package/lib/epoch/processPtcWindow.d.ts +11 -0
- package/lib/epoch/processPtcWindow.d.ts.map +1 -0
- package/lib/epoch/processPtcWindow.js +28 -0
- package/lib/epoch/processPtcWindow.js.map +1 -0
- package/lib/lightClient/spec/index.d.ts +22 -0
- package/lib/lightClient/spec/index.d.ts.map +1 -0
- package/lib/lightClient/spec/index.js +58 -0
- package/lib/lightClient/spec/index.js.map +1 -0
- package/lib/lightClient/spec/isBetterUpdate.d.ts +23 -0
- package/lib/lightClient/spec/isBetterUpdate.d.ts.map +1 -0
- package/lib/lightClient/spec/isBetterUpdate.js +66 -0
- package/lib/lightClient/spec/isBetterUpdate.js.map +1 -0
- package/lib/lightClient/spec/processLightClientUpdate.d.ts +12 -0
- package/lib/lightClient/spec/processLightClientUpdate.d.ts.map +1 -0
- package/lib/lightClient/spec/processLightClientUpdate.js +80 -0
- package/lib/lightClient/spec/processLightClientUpdate.js.map +1 -0
- package/lib/lightClient/spec/store.d.ts +45 -0
- package/lib/lightClient/spec/store.d.ts.map +1 -0
- package/lib/lightClient/spec/store.js +56 -0
- package/lib/lightClient/spec/store.js.map +1 -0
- package/lib/lightClient/spec/utils.d.ts +47 -0
- package/lib/lightClient/spec/utils.d.ts.map +1 -0
- package/lib/lightClient/spec/utils.js +197 -0
- package/lib/lightClient/spec/utils.js.map +1 -0
- package/lib/lightClient/spec/validateLightClientBootstrap.d.ts +4 -0
- package/lib/lightClient/spec/validateLightClientBootstrap.d.ts.map +1 -0
- package/lib/lightClient/spec/validateLightClientBootstrap.js +22 -0
- package/lib/lightClient/spec/validateLightClientBootstrap.js.map +1 -0
- package/lib/lightClient/spec/validateLightClientUpdate.d.ts +5 -0
- package/lib/lightClient/spec/validateLightClientUpdate.d.ts.map +1 -0
- package/lib/lightClient/spec/validateLightClientUpdate.js +88 -0
- package/lib/lightClient/spec/validateLightClientUpdate.js.map +1 -0
- package/lib/signatureSets/executionPayloadEnvelope.js +1 -1
- package/lib/signatureSets/executionPayloadEnvelope.js.map +1 -1
- package/lib/signatureSets/index.d.ts +1 -0
- package/lib/signatureSets/index.d.ts.map +1 -1
- package/lib/signatureSets/index.js +1 -0
- package/lib/signatureSets/index.js.map +1 -1
- package/lib/signatureSets/proposerPreferences.d.ts +4 -0
- package/lib/signatureSets/proposerPreferences.d.ts.map +1 -0
- package/lib/signatureSets/proposerPreferences.js +8 -0
- package/lib/signatureSets/proposerPreferences.js.map +1 -0
- package/lib/slot/upgradeStateToElectra.d.ts.map +1 -1
- package/lib/slot/upgradeStateToElectra.js +2 -2
- package/lib/slot/upgradeStateToElectra.js.map +1 -1
- package/lib/slot/upgradeStateToGloas.d.ts.map +1 -1
- package/lib/slot/upgradeStateToGloas.js +3 -1
- package/lib/slot/upgradeStateToGloas.js.map +1 -1
- package/lib/stateTransition.js +1 -1
- package/lib/stateTransition.js.map +1 -1
- package/lib/stateView/beaconStateView.d.ts +21 -14
- package/lib/stateView/beaconStateView.d.ts.map +1 -1
- package/lib/stateView/beaconStateView.js +63 -39
- package/lib/stateView/beaconStateView.js.map +1 -1
- package/lib/stateView/interface.d.ts +22 -12
- package/lib/stateView/interface.d.ts.map +1 -1
- package/lib/stateView/interface.js.map +1 -1
- package/lib/util/computeAnchorCheckpoint.d.ts +1 -1
- package/lib/util/computeAnchorCheckpoint.d.ts.map +1 -1
- package/lib/util/computeAnchorCheckpoint.js +6 -19
- package/lib/util/computeAnchorCheckpoint.js.map +1 -1
- package/lib/util/epoch.d.ts.map +1 -1
- package/lib/util/epoch.js +6 -4
- package/lib/util/epoch.js.map +1 -1
- package/lib/util/gloas.d.ts +7 -3
- package/lib/util/gloas.d.ts.map +1 -1
- package/lib/util/gloas.js +25 -3
- package/lib/util/gloas.js.map +1 -1
- package/lib/util/loadState/loadState.js +4 -4
- package/lib/util/loadState/loadState.js.map +1 -1
- package/lib/util/validator.d.ts +14 -2
- package/lib/util/validator.d.ts.map +1 -1
- package/lib/util/validator.js +24 -2
- package/lib/util/validator.js.map +1 -1
- package/package.json +14 -9
- package/src/block/index.ts +12 -4
- package/src/block/processConsolidationRequest.ts +2 -1
- package/src/block/processParentExecutionPayload.ts +116 -0
- package/src/block/processWithdrawals.ts +12 -4
- package/src/cache/epochCache.ts +35 -33
- package/src/cache/epochTransitionCache.ts +7 -0
- package/src/epoch/index.ts +9 -0
- package/src/epoch/processPendingDeposits.ts +5 -2
- package/src/epoch/processPtcWindow.ts +38 -0
- package/src/lightClient/spec/index.ts +101 -0
- package/src/lightClient/spec/isBetterUpdate.ts +94 -0
- package/src/lightClient/spec/processLightClientUpdate.ts +119 -0
- package/src/lightClient/spec/store.ts +106 -0
- package/src/lightClient/spec/utils.ts +317 -0
- package/src/lightClient/spec/validateLightClientBootstrap.ts +39 -0
- package/src/lightClient/spec/validateLightClientUpdate.ts +145 -0
- package/src/signatureSets/executionPayloadEnvelope.ts +1 -1
- package/src/signatureSets/index.ts +1 -0
- package/src/signatureSets/proposerPreferences.ts +12 -0
- package/src/slot/upgradeStateToElectra.ts +4 -2
- package/src/slot/upgradeStateToGloas.ts +5 -1
- package/src/stateTransition.ts +1 -1
- package/src/stateView/beaconStateView.ts +72 -52
- package/src/stateView/interface.ts +29 -15
- package/src/util/computeAnchorCheckpoint.ts +6 -19
- package/src/util/epoch.ts +13 -4
- package/src/util/gloas.ts +46 -4
- package/src/util/loadState/loadState.ts +4 -4
- package/src/util/validator.ts +42 -2
- package/lib/block/processExecutionPayloadEnvelope.d.ts +0 -9
- package/lib/block/processExecutionPayloadEnvelope.d.ts.map +0 -1
- package/lib/block/processExecutionPayloadEnvelope.js +0 -106
- package/lib/block/processExecutionPayloadEnvelope.js.map +0 -1
- package/src/block/processExecutionPayloadEnvelope.ts +0 -175
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import {PublicKey, Signature, fastAggregateVerify} from "@chainsafe/blst";
|
|
2
|
+
import {ChainForkConfig} from "@lodestar/config";
|
|
3
|
+
import {
|
|
4
|
+
DOMAIN_SYNC_COMMITTEE,
|
|
5
|
+
FINALIZED_ROOT_DEPTH,
|
|
6
|
+
FINALIZED_ROOT_DEPTH_ELECTRA,
|
|
7
|
+
FINALIZED_ROOT_INDEX,
|
|
8
|
+
FINALIZED_ROOT_INDEX_ELECTRA,
|
|
9
|
+
GENESIS_SLOT,
|
|
10
|
+
MIN_SYNC_COMMITTEE_PARTICIPANTS,
|
|
11
|
+
NEXT_SYNC_COMMITTEE_DEPTH,
|
|
12
|
+
NEXT_SYNC_COMMITTEE_DEPTH_ELECTRA,
|
|
13
|
+
NEXT_SYNC_COMMITTEE_INDEX,
|
|
14
|
+
NEXT_SYNC_COMMITTEE_INDEX_ELECTRA,
|
|
15
|
+
} from "@lodestar/params";
|
|
16
|
+
import {LightClientUpdate, Root, isElectraLightClientUpdate, ssz} from "@lodestar/types";
|
|
17
|
+
import type {ILightClientStore, SyncCommitteeFast} from "./store.js";
|
|
18
|
+
import {
|
|
19
|
+
ZERO_HASH,
|
|
20
|
+
getParticipantPubkeys,
|
|
21
|
+
isFinalityUpdate,
|
|
22
|
+
isSyncCommitteeUpdate,
|
|
23
|
+
isValidLightClientHeader,
|
|
24
|
+
isValidMerkleBranch,
|
|
25
|
+
isZeroedHeader,
|
|
26
|
+
isZeroedSyncCommittee,
|
|
27
|
+
sumBits,
|
|
28
|
+
} from "./utils.js";
|
|
29
|
+
|
|
30
|
+
export function validateLightClientUpdate(
|
|
31
|
+
config: ChainForkConfig,
|
|
32
|
+
store: ILightClientStore,
|
|
33
|
+
update: LightClientUpdate,
|
|
34
|
+
syncCommittee: SyncCommitteeFast
|
|
35
|
+
): void {
|
|
36
|
+
// Verify sync committee has sufficient participants
|
|
37
|
+
if (sumBits(update.syncAggregate.syncCommitteeBits) < MIN_SYNC_COMMITTEE_PARTICIPANTS) {
|
|
38
|
+
throw Error("Sync committee has not sufficient participants");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!isValidLightClientHeader(config, update.attestedHeader)) {
|
|
42
|
+
throw Error("Attested Header is not Valid Light Client Header");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Sanity check that slots are in correct order
|
|
46
|
+
if (update.signatureSlot <= update.attestedHeader.beacon.slot) {
|
|
47
|
+
throw Error(
|
|
48
|
+
`signature slot ${update.signatureSlot} must be after attested header slot ${update.attestedHeader.beacon.slot}`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
if (update.attestedHeader.beacon.slot < update.finalizedHeader.beacon.slot) {
|
|
52
|
+
throw Error(
|
|
53
|
+
`attested header slot ${update.signatureSlot} must be after finalized header slot ${update.finalizedHeader.beacon.slot}`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Verify that the `finality_branch`, if present, confirms `finalized_header`
|
|
58
|
+
// to match the finalized checkpoint root saved in the state of `attested_header`.
|
|
59
|
+
// Note that the genesis finalized checkpoint root is represented as a zero hash.
|
|
60
|
+
if (!isFinalityUpdate(update)) {
|
|
61
|
+
if (!isZeroedHeader(update.finalizedHeader.beacon)) {
|
|
62
|
+
throw Error("finalizedHeader must be zero for non-finality update");
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
let finalizedRoot: Root;
|
|
66
|
+
|
|
67
|
+
if (update.finalizedHeader.beacon.slot === GENESIS_SLOT) {
|
|
68
|
+
if (!isZeroedHeader(update.finalizedHeader.beacon)) {
|
|
69
|
+
throw Error("finalizedHeader must be zero for not finality update");
|
|
70
|
+
}
|
|
71
|
+
finalizedRoot = ZERO_HASH;
|
|
72
|
+
} else {
|
|
73
|
+
if (!isValidLightClientHeader(config, update.finalizedHeader)) {
|
|
74
|
+
throw Error("Finalized Header is not valid Light Client Header");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
finalizedRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(update.finalizedHeader.beacon);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (
|
|
81
|
+
!isValidMerkleBranch(
|
|
82
|
+
finalizedRoot,
|
|
83
|
+
update.finalityBranch,
|
|
84
|
+
isElectraLightClientUpdate(update) ? FINALIZED_ROOT_DEPTH_ELECTRA : FINALIZED_ROOT_DEPTH,
|
|
85
|
+
isElectraLightClientUpdate(update) ? FINALIZED_ROOT_INDEX_ELECTRA : FINALIZED_ROOT_INDEX,
|
|
86
|
+
update.attestedHeader.beacon.stateRoot
|
|
87
|
+
)
|
|
88
|
+
) {
|
|
89
|
+
throw Error("Invalid finality header merkle branch");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Verify that the `next_sync_committee`, if present, actually is the next sync committee saved in the
|
|
94
|
+
// state of the `attested_header`
|
|
95
|
+
if (!isSyncCommitteeUpdate(update)) {
|
|
96
|
+
if (!isZeroedSyncCommittee(update.nextSyncCommittee)) {
|
|
97
|
+
throw Error("nextSyncCommittee must be zero for non sync committee update");
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
if (
|
|
101
|
+
!isValidMerkleBranch(
|
|
102
|
+
ssz.altair.SyncCommittee.hashTreeRoot(update.nextSyncCommittee),
|
|
103
|
+
update.nextSyncCommitteeBranch,
|
|
104
|
+
isElectraLightClientUpdate(update) ? NEXT_SYNC_COMMITTEE_DEPTH_ELECTRA : NEXT_SYNC_COMMITTEE_DEPTH,
|
|
105
|
+
isElectraLightClientUpdate(update) ? NEXT_SYNC_COMMITTEE_INDEX_ELECTRA : NEXT_SYNC_COMMITTEE_INDEX,
|
|
106
|
+
update.attestedHeader.beacon.stateRoot
|
|
107
|
+
)
|
|
108
|
+
) {
|
|
109
|
+
throw Error("Invalid next sync committee merkle branch");
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Verify sync committee aggregate signature
|
|
114
|
+
|
|
115
|
+
const participantPubkeys = getParticipantPubkeys(syncCommittee.pubkeys, update.syncAggregate.syncCommitteeBits);
|
|
116
|
+
|
|
117
|
+
const signingRoot = ssz.phase0.SigningData.hashTreeRoot({
|
|
118
|
+
objectRoot: ssz.phase0.BeaconBlockHeader.hashTreeRoot(update.attestedHeader.beacon),
|
|
119
|
+
domain: store.config.getDomain(update.signatureSlot - 1, DOMAIN_SYNC_COMMITTEE),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
if (!isValidBlsAggregate(participantPubkeys, signingRoot, update.syncAggregate.syncCommitteeSignature)) {
|
|
123
|
+
throw Error("Invalid aggregate signature");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Same as BLS.verifyAggregate but with detailed error messages
|
|
129
|
+
*/
|
|
130
|
+
function isValidBlsAggregate(publicKeys: PublicKey[], message: Uint8Array, signature: Uint8Array): boolean {
|
|
131
|
+
let sig: Signature;
|
|
132
|
+
try {
|
|
133
|
+
sig = Signature.fromBytes(signature, true);
|
|
134
|
+
} catch (e) {
|
|
135
|
+
(e as Error).message = `Error deserializing signature: ${(e as Error).message}`;
|
|
136
|
+
throw e;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
try {
|
|
140
|
+
return fastAggregateVerify(message, publicKeys, sig);
|
|
141
|
+
} catch (e) {
|
|
142
|
+
(e as Error).message = `Error verifying signature: ${(e as Error).message}`;
|
|
143
|
+
throw e;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -11,7 +11,7 @@ export function getExecutionPayloadEnvelopeSigningRoot(
|
|
|
11
11
|
config: BeaconConfig,
|
|
12
12
|
envelope: gloas.ExecutionPayloadEnvelope
|
|
13
13
|
): Uint8Array {
|
|
14
|
-
const domain = config.getDomain(envelope.
|
|
14
|
+
const domain = config.getDomain(envelope.payload.slotNumber, DOMAIN_BEACON_BUILDER);
|
|
15
15
|
|
|
16
16
|
return computeSigningRoot(ssz.gloas.ExecutionPayloadEnvelope, envelope, domain);
|
|
17
17
|
}
|
|
@@ -20,6 +20,7 @@ export * from "./executionPayloadEnvelope.js";
|
|
|
20
20
|
export * from "./indexedAttestation.js";
|
|
21
21
|
export * from "./indexedPayloadAttestation.js";
|
|
22
22
|
export * from "./proposer.js";
|
|
23
|
+
export * from "./proposerPreferences.js";
|
|
23
24
|
export * from "./proposerSlashings.js";
|
|
24
25
|
export * from "./randao.js";
|
|
25
26
|
export * from "./voluntaryExits.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {BeaconConfig} from "@lodestar/config";
|
|
2
|
+
import {DOMAIN_PROPOSER_PREFERENCES} from "@lodestar/params";
|
|
3
|
+
import {gloas, ssz} from "@lodestar/types";
|
|
4
|
+
import {computeSigningRoot} from "../util/index.js";
|
|
5
|
+
|
|
6
|
+
export function getProposerPreferencesSigningRoot(
|
|
7
|
+
config: BeaconConfig,
|
|
8
|
+
preferences: gloas.ProposerPreferences
|
|
9
|
+
): Uint8Array {
|
|
10
|
+
const domain = config.getDomain(preferences.proposalSlot, DOMAIN_PROPOSER_PREFERENCES);
|
|
11
|
+
return computeSigningRoot(ssz.gloas.ProposerPreferences, preferences, domain);
|
|
12
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {FAR_FUTURE_EPOCH, GENESIS_SLOT, UNSET_DEPOSIT_REQUESTS_START_INDEX} from "@lodestar/params";
|
|
1
|
+
import {FAR_FUTURE_EPOCH, ForkSeq, GENESIS_SLOT, UNSET_DEPOSIT_REQUESTS_START_INDEX} from "@lodestar/params";
|
|
2
2
|
import {ValidatorIndex, ssz} from "@lodestar/types";
|
|
3
3
|
import {CachedBeaconStateElectra, getCachedBeaconState} from "../cache/stateCache.js";
|
|
4
4
|
import {G2_POINT_AT_INFINITY} from "../constants/constants.js";
|
|
@@ -78,7 +78,9 @@ export function upgradeStateToElectra(stateDeneb: CachedBeaconStateDeneb): Cache
|
|
|
78
78
|
stateElectraView.commit();
|
|
79
79
|
const tmpElectraState = getCachedBeaconState(stateElectraView, stateDeneb);
|
|
80
80
|
stateElectraView.exitBalanceToConsume = BigInt(getActivationExitChurnLimit(tmpElectraState.epochCtx));
|
|
81
|
-
stateElectraView.consolidationBalanceToConsume = BigInt(
|
|
81
|
+
stateElectraView.consolidationBalanceToConsume = BigInt(
|
|
82
|
+
getConsolidationChurnLimit(ForkSeq.electra, tmpElectraState.epochCtx)
|
|
83
|
+
);
|
|
82
84
|
|
|
83
85
|
preActivation.sort((i0, i1) => {
|
|
84
86
|
const res = validatorsArr[i0].activationEligibilityEpoch - validatorsArr[i1].activationEligibilityEpoch;
|
|
@@ -5,7 +5,7 @@ import {isValidDepositSignature} from "../block/processDeposit.js";
|
|
|
5
5
|
import {applyDepositForBuilder} from "../block/processDepositRequest.js";
|
|
6
6
|
import {getCachedBeaconState} from "../cache/stateCache.js";
|
|
7
7
|
import {CachedBeaconStateFulu, CachedBeaconStateGloas} from "../types.js";
|
|
8
|
-
import {isBuilderWithdrawalCredential} from "../util/gloas.js";
|
|
8
|
+
import {initializePtcWindow, isBuilderWithdrawalCredential} from "../util/gloas.js";
|
|
9
9
|
import {isValidatorKnown} from "../util/index.js";
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -48,6 +48,9 @@ export function upgradeStateToGloas(stateFulu: CachedBeaconStateFulu): CachedBea
|
|
|
48
48
|
stateGloasView.currentSyncCommittee = stateGloasCloned.currentSyncCommittee;
|
|
49
49
|
stateGloasView.nextSyncCommittee = stateGloasCloned.nextSyncCommittee;
|
|
50
50
|
stateGloasView.latestExecutionPayloadBid.blockHash = stateFulu.latestExecutionPayloadHeader.blockHash;
|
|
51
|
+
stateGloasView.latestExecutionPayloadBid.executionRequestsRoot = ssz.electra.ExecutionRequests.hashTreeRoot(
|
|
52
|
+
ssz.electra.ExecutionRequests.defaultValue()
|
|
53
|
+
);
|
|
51
54
|
stateGloasView.nextWithdrawalIndex = stateGloasCloned.nextWithdrawalIndex;
|
|
52
55
|
stateGloasView.nextWithdrawalValidatorIndex = stateGloasCloned.nextWithdrawalValidatorIndex;
|
|
53
56
|
stateGloasView.historicalSummaries = stateGloasCloned.historicalSummaries;
|
|
@@ -61,6 +64,7 @@ export function upgradeStateToGloas(stateFulu: CachedBeaconStateFulu): CachedBea
|
|
|
61
64
|
stateGloasView.pendingPartialWithdrawals = stateGloasCloned.pendingPartialWithdrawals;
|
|
62
65
|
stateGloasView.pendingConsolidations = stateGloasCloned.pendingConsolidations;
|
|
63
66
|
stateGloasView.proposerLookahead = stateGloasCloned.proposerLookahead;
|
|
67
|
+
stateGloasView.ptcWindow = ssz.gloas.PtcWindow.toViewDU(initializePtcWindow(stateFulu));
|
|
64
68
|
|
|
65
69
|
for (let i = 0; i < SLOTS_PER_HISTORICAL_ROOT; i++) {
|
|
66
70
|
stateGloasView.executionPayloadAvailability.set(i, true);
|
package/src/stateTransition.ts
CHANGED
|
@@ -282,7 +282,7 @@ function processSlotsWithTransientCache(
|
|
|
282
282
|
{
|
|
283
283
|
const timer = metrics?.epochTransitionStepTime.startTimer({step: EpochTransitionStep.finalProcessEpoch});
|
|
284
284
|
// last step to prepare epoch data that depends on the upgraded state, for example proposerLookahead of BeaconStateFulu
|
|
285
|
-
postState.epochCtx.finalProcessEpoch(postState);
|
|
285
|
+
postState.epochCtx.finalProcessEpoch(postState, epochTransitionCache);
|
|
286
286
|
timer?.();
|
|
287
287
|
}
|
|
288
288
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {CompactMultiProof, ProofType, Tree, createProof} from "@chainsafe/persistent-merkle-tree";
|
|
2
2
|
import {BitArray, ByteViews} from "@chainsafe/ssz";
|
|
3
3
|
import {BeaconConfig} from "@lodestar/config";
|
|
4
|
-
import {ForkName, ForkSeq, SLOTS_PER_HISTORICAL_ROOT
|
|
4
|
+
import {ForkName, ForkSeq, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
|
|
5
5
|
import {
|
|
6
6
|
BeaconBlock,
|
|
7
7
|
BeaconState,
|
|
@@ -28,8 +28,7 @@ import {
|
|
|
28
28
|
rewards,
|
|
29
29
|
} from "@lodestar/types";
|
|
30
30
|
import {Checkpoint, Fork} from "@lodestar/types/phase0";
|
|
31
|
-
import {
|
|
32
|
-
import {ProcessExecutionPayloadEnvelopeOpts} from "../block/processExecutionPayloadEnvelope.js";
|
|
31
|
+
import {applyParentExecutionPayload} from "../block/processParentExecutionPayload.js";
|
|
33
32
|
import {VoluntaryExitValidity, getVoluntaryExitValidity} from "../block/processVoluntaryExit.js";
|
|
34
33
|
import {getExpectedWithdrawals} from "../block/processWithdrawals.js";
|
|
35
34
|
import {EffectiveBalanceIncrements} from "../cache/effectiveBalanceIncrements.js";
|
|
@@ -69,7 +68,7 @@ import {canBuilderCoverBid} from "../util/gloas.js";
|
|
|
69
68
|
import {loadState} from "../util/loadState/loadState.js";
|
|
70
69
|
import {getRandaoMix} from "../util/seed.js";
|
|
71
70
|
import {getLatestWeakSubjectivityCheckpointEpoch} from "../util/weakSubjectivity.js";
|
|
72
|
-
import {IBeaconStateView, IBeaconStateViewLatestFork} from "./interface.js";
|
|
71
|
+
import {IBeaconStateView, IBeaconStateViewGloas, IBeaconStateViewLatestFork, isStatePostGloas} from "./interface.js";
|
|
73
72
|
|
|
74
73
|
export class BeaconStateView implements IBeaconStateViewLatestFork {
|
|
75
74
|
private readonly config: BeaconConfig;
|
|
@@ -84,8 +83,6 @@ export class BeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
84
83
|
private _currentEpochParticipation: Uint8Array | null = null;
|
|
85
84
|
// bellatrix
|
|
86
85
|
private _latestExecutionPayloadHeader: ExecutionPayloadHeader | null = null;
|
|
87
|
-
// Caches the cross-fork latestBlockHash value
|
|
88
|
-
private _latestBlockHash: Bytes32 | null = null;
|
|
89
86
|
// capella
|
|
90
87
|
private _historicalSummaries: capella.HistoricalSummaries | null = null;
|
|
91
88
|
// electra
|
|
@@ -218,9 +215,13 @@ export class BeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
218
215
|
// bellatrix
|
|
219
216
|
|
|
220
217
|
get latestExecutionPayloadHeader(): ExecutionPayloadHeader {
|
|
221
|
-
|
|
218
|
+
const forkSeq = this.config.getForkSeq(this.cachedState.slot);
|
|
219
|
+
if (forkSeq < ForkSeq.bellatrix) {
|
|
222
220
|
throw new Error("latestExecutionPayloadHeader is not available before Bellatrix");
|
|
223
221
|
}
|
|
222
|
+
if (forkSeq >= ForkSeq.gloas) {
|
|
223
|
+
throw new Error("latestExecutionPayloadHeader is not available after Gloas");
|
|
224
|
+
}
|
|
224
225
|
|
|
225
226
|
if (this._latestExecutionPayloadHeader === null) {
|
|
226
227
|
this._latestExecutionPayloadHeader = (
|
|
@@ -231,30 +232,6 @@ export class BeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
231
232
|
return this._latestExecutionPayloadHeader;
|
|
232
233
|
}
|
|
233
234
|
|
|
234
|
-
/**
|
|
235
|
-
* Cross-fork accessor for the execution block hash of the most recently included payload.
|
|
236
|
-
* Pre-gloas: reads from latestExecutionPayloadHeader.blockHash.
|
|
237
|
-
* Gloas+: reads the dedicated latestBlockHash field (EIP-7732).
|
|
238
|
-
*/
|
|
239
|
-
get latestBlockHash(): Bytes32 {
|
|
240
|
-
const forkSeq = this.config.getForkSeq(this.cachedState.slot);
|
|
241
|
-
if (forkSeq < ForkSeq.bellatrix) {
|
|
242
|
-
throw new Error("latestBlockHash is not available before Bellatrix");
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (this._latestBlockHash === null) {
|
|
246
|
-
if (forkSeq >= ForkSeq.gloas) {
|
|
247
|
-
this._latestBlockHash = (this.cachedState as CachedBeaconStateGloas).latestBlockHash;
|
|
248
|
-
} else {
|
|
249
|
-
this._latestBlockHash = (
|
|
250
|
-
this.cachedState as CachedBeaconStateExecutions
|
|
251
|
-
).latestExecutionPayloadHeader.blockHash;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
return this._latestBlockHash;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
235
|
/**
|
|
259
236
|
* The execution block number of the most recently included payload.
|
|
260
237
|
* Named payloadBlockNumber (not latestBlockNumber) to mirror ExecutionPayloadHeader.blockNumber pre-gloas.
|
|
@@ -365,6 +342,13 @@ export class BeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
365
342
|
|
|
366
343
|
// gloas
|
|
367
344
|
|
|
345
|
+
get latestBlockHash(): Bytes32 {
|
|
346
|
+
if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
|
|
347
|
+
throw new Error("latestBlockHash is not available before Gloas");
|
|
348
|
+
}
|
|
349
|
+
return (this.cachedState as CachedBeaconStateGloas).latestBlockHash;
|
|
350
|
+
}
|
|
351
|
+
|
|
368
352
|
get executionPayloadAvailability(): BitArray {
|
|
369
353
|
if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
|
|
370
354
|
throw new Error("executionPayloadAvailability is not available before Gloas");
|
|
@@ -422,16 +406,44 @@ export class BeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
422
406
|
}
|
|
423
407
|
|
|
424
408
|
/**
|
|
425
|
-
* Return the
|
|
426
|
-
* return -1 if validator is not in the PTC committee for the given slot.
|
|
409
|
+
* Return the PTCs for an epoch
|
|
427
410
|
*/
|
|
428
|
-
|
|
411
|
+
getEpochPTCs(epoch: Epoch): Uint32Array[] {
|
|
412
|
+
if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
|
|
413
|
+
throw new Error("PTC committees are not supported before Gloas");
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const epochCtx = (this.cachedState as CachedBeaconStateGloas).epochCtx;
|
|
417
|
+
if (epoch === epochCtx.epoch) {
|
|
418
|
+
return epochCtx.payloadTimelinessCommittees;
|
|
419
|
+
}
|
|
420
|
+
if (epoch === epochCtx.nextEpoch) {
|
|
421
|
+
return epochCtx.nextPayloadTimelinessCommittees;
|
|
422
|
+
}
|
|
423
|
+
throw new Error(`PTC committees are not available for epoch=${epoch}`);
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Return all positions of the validator in the PTC committee for the given slot.
|
|
427
|
+
*
|
|
428
|
+
* `compute_ptc` samples by effective balance and may place the same validator at multiple
|
|
429
|
+
* positions, so a validator can have more than one index. Returns an empty array if the
|
|
430
|
+
* validator is not in the PTC for the given slot.
|
|
431
|
+
*
|
|
432
|
+
* Spec: gloas/fork-choice.md#new-on_payload_attestation_message
|
|
433
|
+
*/
|
|
434
|
+
getIndicesInPayloadTimelinessCommittee(validatorIndex: ValidatorIndex, slot: Slot): number[] {
|
|
429
435
|
if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
|
|
430
436
|
throw new Error("PTC committees are not supported before Gloas");
|
|
431
437
|
}
|
|
432
438
|
|
|
433
439
|
const ptcCommittee = (this.cachedState as CachedBeaconStateGloas).epochCtx.getPayloadTimelinessCommittee(slot);
|
|
434
|
-
|
|
440
|
+
const indices: number[] = [];
|
|
441
|
+
for (let i = 0; i < ptcCommittee.length; i++) {
|
|
442
|
+
if (ptcCommittee[i] === validatorIndex) {
|
|
443
|
+
indices.push(i);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return indices;
|
|
435
447
|
}
|
|
436
448
|
|
|
437
449
|
// Shuffling and committees
|
|
@@ -719,7 +731,11 @@ export class BeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
719
731
|
|
|
720
732
|
// Serialization
|
|
721
733
|
|
|
722
|
-
loadOtherState(
|
|
734
|
+
loadOtherState(
|
|
735
|
+
stateBytes: Uint8Array,
|
|
736
|
+
seedValidatorsBytes?: Uint8Array,
|
|
737
|
+
opts?: {preloadValidatorsAndBalances?: boolean}
|
|
738
|
+
): IBeaconStateView {
|
|
723
739
|
const {state} = loadState(this.config, this.cachedState, stateBytes, seedValidatorsBytes);
|
|
724
740
|
|
|
725
741
|
const cachedState = createCachedBeaconState(
|
|
@@ -734,9 +750,10 @@ export class BeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
734
750
|
}
|
|
735
751
|
);
|
|
736
752
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
753
|
+
if (opts?.preloadValidatorsAndBalances) {
|
|
754
|
+
cachedState.validators.getAllReadonlyValues();
|
|
755
|
+
cachedState.balances.getAll();
|
|
756
|
+
}
|
|
740
757
|
|
|
741
758
|
return new BeaconStateView(cachedState);
|
|
742
759
|
}
|
|
@@ -794,19 +811,22 @@ export class BeaconStateView implements IBeaconStateViewLatestFork {
|
|
|
794
811
|
return new BeaconStateView(newState);
|
|
795
812
|
}
|
|
796
813
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
):
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
throw Error(`processExecutionPayloadEnvelope is only available for gloas+ forks, got fork=${fork}`);
|
|
814
|
+
/**
|
|
815
|
+
* Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.5/specs/gloas/validator.md#executionpayload
|
|
816
|
+
*/
|
|
817
|
+
withParentPayloadApplied(executionRequests: electra.ExecutionRequests): IBeaconStateViewGloas {
|
|
818
|
+
if (this.config.getForkSeq(this.cachedState.slot) < ForkSeq.gloas) {
|
|
819
|
+
throw new Error("withParentPayloadApplied is not available before Gloas");
|
|
804
820
|
}
|
|
805
|
-
const
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
);
|
|
810
|
-
|
|
821
|
+
const stateCopy = this.cachedState.clone(true) as CachedBeaconStateGloas;
|
|
822
|
+
|
|
823
|
+
applyParentExecutionPayload(stateCopy, executionRequests);
|
|
824
|
+
|
|
825
|
+
const stateView = new BeaconStateView(stateCopy);
|
|
826
|
+
if (!isStatePostGloas(stateView)) {
|
|
827
|
+
throw new Error("Expected gloas state after clone");
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
return stateView;
|
|
811
831
|
}
|
|
812
832
|
}
|
|
@@ -41,7 +41,6 @@ import {
|
|
|
41
41
|
rewards,
|
|
42
42
|
} from "@lodestar/types";
|
|
43
43
|
import {Checkpoint, Fork} from "@lodestar/types/phase0";
|
|
44
|
-
import {ProcessExecutionPayloadEnvelopeOpts} from "../block/processExecutionPayloadEnvelope.js";
|
|
45
44
|
import {VoluntaryExitValidity} from "../block/processVoluntaryExit.js";
|
|
46
45
|
import {EffectiveBalanceIncrements} from "../cache/effectiveBalanceIncrements.js";
|
|
47
46
|
import {EpochTransitionCacheOpts} from "../cache/epochTransitionCache.js";
|
|
@@ -138,7 +137,13 @@ export interface IBeaconStateView {
|
|
|
138
137
|
isStateValidatorsNodesPopulated(): boolean;
|
|
139
138
|
|
|
140
139
|
// Serialization
|
|
141
|
-
|
|
140
|
+
/** Set `preloadValidatorsAndBalances` only when the whole state will be consumed
|
|
141
|
+
* immediately (e.g. CP reload before block replay). */
|
|
142
|
+
loadOtherState(
|
|
143
|
+
stateBytes: Uint8Array,
|
|
144
|
+
seedValidatorsBytes?: Uint8Array,
|
|
145
|
+
opts?: {preloadValidatorsAndBalances?: boolean}
|
|
146
|
+
): IBeaconStateView;
|
|
142
147
|
toValue(): BeaconState;
|
|
143
148
|
serialize(): Uint8Array;
|
|
144
149
|
serializedSize(): number;
|
|
@@ -187,13 +192,6 @@ export interface IBeaconStateViewAltair extends IBeaconStateView {
|
|
|
187
192
|
export interface IBeaconStateViewBellatrix extends IBeaconStateViewAltair {
|
|
188
193
|
forkName: ForkPostBellatrix;
|
|
189
194
|
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
195
|
/**
|
|
198
196
|
* The execution block number of the most recently included payload.
|
|
199
197
|
* Named payloadBlockNumber (not latestBlockNumber) to mirror ExecutionPayloadHeader.blockNumber pre-gloas.
|
|
@@ -244,16 +242,25 @@ export interface IBeaconStateViewFulu extends IBeaconStateViewElectra {
|
|
|
244
242
|
/** Gloas+ state fields — use isStatePostGloas() guard */
|
|
245
243
|
export interface IBeaconStateViewGloas extends IBeaconStateViewFulu {
|
|
246
244
|
forkName: ForkPostGloas;
|
|
245
|
+
/** Removed from BeaconState in gloas. Use `latestBlockHash` instead. */
|
|
246
|
+
latestExecutionPayloadHeader: never;
|
|
247
|
+
/** Removed from BeaconState in gloas. */
|
|
248
|
+
payloadBlockNumber: never;
|
|
249
|
+
latestBlockHash: Bytes32;
|
|
247
250
|
executionPayloadAvailability: BitArray;
|
|
248
251
|
latestExecutionPayloadBid: ExecutionPayloadBid;
|
|
249
252
|
payloadExpectedWithdrawals: capella.Withdrawal[];
|
|
250
253
|
getBuilder(index: BuilderIndex): gloas.Builder;
|
|
251
254
|
canBuilderCoverBid(builderIndex: BuilderIndex, bidAmount: number): boolean;
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
255
|
+
getEpochPTCs(epoch: Epoch): Uint32Array[];
|
|
256
|
+
getIndicesInPayloadTimelinessCommittee(validatorIndex: ValidatorIndex, slot: Slot): number[];
|
|
257
|
+
/**
|
|
258
|
+
* Clone the state and apply parent execution payload effects.
|
|
259
|
+
* Used during block production and prepareNextSlot so that withdrawals and
|
|
260
|
+
* operation selection (e.g. voluntary exits) see the same post-apply state that the block
|
|
261
|
+
* processor will see at import.
|
|
262
|
+
*/
|
|
263
|
+
withParentPayloadApplied(executionRequests: electra.ExecutionRequests): IBeaconStateViewGloas;
|
|
257
264
|
}
|
|
258
265
|
|
|
259
266
|
/**
|
|
@@ -262,7 +269,14 @@ export interface IBeaconStateViewGloas extends IBeaconStateViewFulu {
|
|
|
262
269
|
* forkName as ForkName since the class wraps any fork's state.
|
|
263
270
|
* Sub-interfaces retain their narrowed forkName discriminants for caller-side type guards.
|
|
264
271
|
*/
|
|
265
|
-
export type IBeaconStateViewLatestFork = Omit<
|
|
272
|
+
export type IBeaconStateViewLatestFork = Omit<
|
|
273
|
+
IBeaconStateViewGloas,
|
|
274
|
+
"forkName" | "latestExecutionPayloadHeader" | "payloadBlockNumber"
|
|
275
|
+
> & {
|
|
276
|
+
forkName: ForkName;
|
|
277
|
+
latestExecutionPayloadHeader: ExecutionPayloadHeader;
|
|
278
|
+
payloadBlockNumber: number;
|
|
279
|
+
};
|
|
266
280
|
export function isStatePostAltair(state: IBeaconStateView): state is IBeaconStateViewAltair {
|
|
267
281
|
return isForkPostAltair(state.forkName);
|
|
268
282
|
}
|
|
@@ -1,34 +1,21 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
|
-
import {
|
|
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
|
-
|
|
8
|
+
_config: ChainForkConfig,
|
|
10
9
|
anchorState: BeaconStateAllForks
|
|
11
10
|
): {checkpoint: phase0.Checkpoint; blockHeader: phase0.BeaconBlockHeader} {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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 {
|
|
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 =
|
|
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 =
|