-
-
Notifications
You must be signed in to change notification settings - Fork 317
/
Copy pathinitiateValidatorExit.ts
62 lines (58 loc) · 2.72 KB
/
initiateValidatorExit.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import {CompositeViewDU} from "@chainsafe/ssz";
import {FAR_FUTURE_EPOCH, ForkSeq} from "@lodestar/params";
import {ssz} from "@lodestar/types";
import {CachedBeaconStateAllForks, CachedBeaconStateElectra} from "../types.js";
import {computeExitEpochAndUpdateChurn} from "../util/epoch.js";
/**
* Initiate the exit of the validator with index ``index``.
*
* NOTE: This function takes a `validator` as argument instead of the validator index.
* SSZ TreeViews have a dangerous edge case that may break the code here in a non-obvious way.
* When running `state.validators[i]` you get a SubTree of that validator with a hook to the state.
* Then, when a property of `validator` is set it propagates the changes upwards to the parent tree up to the state.
* This means that `validator` will propagate its new state along with the current state of its parent tree up to
* the state, potentially overwriting changes done in other SubTrees before.
* ```ts
* // default state.validators, all zeroes
* const validatorsA = state.validators
* const validatorsB = state.validators
* validatorsA[0].exitEpoch = 9
* validatorsB[0].exitEpoch = 9 // Setting a value in validatorsB will overwrite all changes from validatorsA
* // validatorsA[0].exitEpoch is 0
* // validatorsB[0].exitEpoch is 9
* ```
* Forcing consumers to pass the SubTree of `validator` directly mitigates this issue.
*/
export function initiateValidatorExit(
fork: ForkSeq,
state: CachedBeaconStateAllForks,
validator: CompositeViewDU<typeof ssz.phase0.Validator>
): void {
const {config, epochCtx} = state;
// return if validator already initiated exit
if (validator.exitEpoch !== FAR_FUTURE_EPOCH) {
return;
}
if (fork < ForkSeq.electra) {
// Limits the number of validators that can exit on each epoch.
// Expects all state.validators to follow this rule, i.e. no validator.exitEpoch is greater than exitQueueEpoch.
// If there the churnLimit is reached at this current exitQueueEpoch, advance epoch and reset churn.
if (epochCtx.exitQueueChurn >= epochCtx.churnLimit) {
epochCtx.exitQueueEpoch += 1;
epochCtx.exitQueueChurn = 1; // = 1 to account for this validator with exitQueueEpoch
} else {
// Add this validator to the current exitQueueEpoch churn
epochCtx.exitQueueChurn += 1;
}
// set validator exit epoch
validator.exitEpoch = epochCtx.exitQueueEpoch;
} else {
// set validator exit epoch
// Note we don't use epochCtx.exitQueueChurn and exitQueueEpoch anymore
validator.exitEpoch = computeExitEpochAndUpdateChurn(
state as CachedBeaconStateElectra,
BigInt(validator.effectiveBalance)
);
}
validator.withdrawableEpoch = validator.exitEpoch + config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY;
}