-
-
Notifications
You must be signed in to change notification settings - Fork 348
/
Copy pathprocessConsolidationRequest.ts
136 lines (113 loc) Β· 4.86 KB
/
processConsolidationRequest.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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import {FAR_FUTURE_EPOCH, MIN_ACTIVATION_BALANCE, PENDING_CONSOLIDATIONS_LIMIT} from "@lodestar/params";
import {electra, ssz} from "@lodestar/types";
import {CachedBeaconStateElectra} from "../types.js";
import {hasEth1WithdrawalCredential} from "../util/capella.js";
import {hasExecutionWithdrawalCredential, isPubkeyKnown, switchToCompoundingValidator} from "../util/electra.js";
import {computeConsolidationEpochAndUpdateChurn} from "../util/epoch.js";
import {getConsolidationChurnLimit, isActiveValidator} from "../util/validator.js";
// TODO Electra: Clean up necessary as there is a lot of overlap with isValidSwitchToCompoundRequest
export function processConsolidationRequest(
state: CachedBeaconStateElectra,
consolidationRequest: electra.ConsolidationRequest
): void {
const {sourcePubkey, targetPubkey, sourceAddress} = consolidationRequest;
if (!isPubkeyKnown(state, sourcePubkey) || !isPubkeyKnown(state, targetPubkey)) {
return;
}
const sourceIndex = state.epochCtx.getValidatorIndex(sourcePubkey);
const targetIndex = state.epochCtx.getValidatorIndex(targetPubkey);
if (sourceIndex === null || targetIndex === null) {
return;
}
if (isValidSwitchToCompoundRequest(state, consolidationRequest)) {
switchToCompoundingValidator(state, sourceIndex);
// Early return since we have already switched validator to compounding
return;
}
// If the pending consolidations queue is full, consolidation requests are ignored
if (state.pendingConsolidations.length >= PENDING_CONSOLIDATIONS_LIMIT) {
return;
}
// If there is too little available consolidation churn limit, consolidation requests are ignored
if (getConsolidationChurnLimit(state.epochCtx) <= MIN_ACTIVATION_BALANCE) {
return;
}
// Verify that source != target, so a consolidation cannot be used as an exit.
if (sourceIndex === targetIndex) {
return;
}
const sourceValidator = state.validators.get(sourceIndex);
const targetValidator = state.validators.getReadonly(targetIndex);
const sourceWithdrawalAddress = sourceValidator.withdrawalCredentials.subarray(12);
const currentEpoch = state.epochCtx.epoch;
// Verify withdrawal credentials
if (
!hasExecutionWithdrawalCredential(sourceValidator.withdrawalCredentials) ||
!hasExecutionWithdrawalCredential(targetValidator.withdrawalCredentials)
) {
return;
}
if (Buffer.compare(sourceWithdrawalAddress, sourceAddress) !== 0) {
return;
}
// Verify the source and the target are active
if (!isActiveValidator(sourceValidator, currentEpoch) || !isActiveValidator(targetValidator, currentEpoch)) {
return;
}
// Verify exits for source and target have not been initiated
if (sourceValidator.exitEpoch !== FAR_FUTURE_EPOCH || targetValidator.exitEpoch !== FAR_FUTURE_EPOCH) {
return;
}
// TODO Electra: See if we can get rid of big int
const exitEpoch = computeConsolidationEpochAndUpdateChurn(state, BigInt(sourceValidator.effectiveBalance));
sourceValidator.exitEpoch = exitEpoch;
sourceValidator.withdrawableEpoch = exitEpoch + state.config.MIN_VALIDATOR_WITHDRAWABILITY_DELAY;
const pendingConsolidation = ssz.electra.PendingConsolidation.toViewDU({
sourceIndex,
targetIndex,
});
state.pendingConsolidations.push(pendingConsolidation);
// Churn any target excess active balance of target and raise its max
if (hasEth1WithdrawalCredential(targetValidator.withdrawalCredentials)) {
switchToCompoundingValidator(state, targetIndex);
}
}
/**
* Determine if we should set consolidation target validator to compounding credential
*/
function isValidSwitchToCompoundRequest(
state: CachedBeaconStateElectra,
consolidationRequest: electra.ConsolidationRequest
): boolean {
const {sourcePubkey, targetPubkey, sourceAddress} = consolidationRequest;
const sourceIndex = state.epochCtx.getValidatorIndex(sourcePubkey);
const targetIndex = state.epochCtx.getValidatorIndex(targetPubkey);
// Verify pubkey exists
if (sourceIndex === null) {
// this check is mainly to make the compiler happy, pubkey is checked by the consumer already
return false;
}
// Switch to compounding requires source and target be equal
if (sourceIndex !== targetIndex) {
return false;
}
const sourceValidator = state.validators.getReadonly(sourceIndex);
const sourceWithdrawalAddress = sourceValidator.withdrawalCredentials.subarray(12);
// Verify request has been authorized
if (Buffer.compare(sourceWithdrawalAddress, sourceAddress) !== 0) {
return false;
}
// Verify source withdrawal credentials
if (!hasEth1WithdrawalCredential(sourceValidator.withdrawalCredentials)) {
return false;
}
// Verify the source is active
if (!isActiveValidator(sourceValidator, state.epochCtx.epoch)) {
return false;
}
// Verify exit for source has not been initiated
if (sourceValidator.exitEpoch !== FAR_FUTURE_EPOCH) {
return false;
}
return true;
}