-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathprocess_withdrawal_request.zig
More file actions
110 lines (96 loc) · 5.03 KB
/
process_withdrawal_request.zig
File metadata and controls
110 lines (96 loc) · 5.03 KB
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
const std = @import("std");
const ForkSeq = @import("config").ForkSeq;
const BeaconConfig = @import("config").BeaconConfig;
const BeaconState = @import("fork_types").BeaconState;
const EpochCache = @import("../cache/epoch_cache.zig").EpochCache;
const c = @import("constants");
const types = @import("consensus_types");
const preset = @import("preset").preset;
const WithdrawalRequest = types.electra.WithdrawalRequest.Type;
const PendingPartialWithdrawal = types.electra.PendingPartialWithdrawal.Type;
const hasCompoundingWithdrawalCredential = @import("../utils/electra.zig").hasCompoundingWithdrawalCredential;
const hasExecutionWithdrawalCredential = @import("../utils/electra.zig").hasExecutionWithdrawalCredential;
const isActiveValidatorView = @import("../validator.zig").isActiveValidatorView;
const getPendingBalanceToWithdraw = @import("../validator.zig").getPendingBalanceToWithdraw;
const initiateValidatorExit = @import("./initiate_validator_exit.zig").initiateValidatorExit;
const computeExitEpochAndUpdateChurn = @import("../utils/epoch.zig").computeExitEpochAndUpdateChurn;
pub fn processWithdrawalRequest(
comptime fork: ForkSeq,
config: *const BeaconConfig,
epoch_cache: *EpochCache,
state: *BeaconState(fork),
withdrawal_request: *const WithdrawalRequest,
) !void {
const amount = withdrawal_request.amount;
// no need to use unfinalized pubkey cache from 6110 as validator won't be active anyway
const pubkey_to_index = epoch_cache.pubkey_to_index;
const is_full_exit_request = amount == c.FULL_EXIT_REQUEST_AMOUNT;
var pending_partial_withdrawals = try state.pendingPartialWithdrawals();
// If partial withdrawal queue is full, only full exits are processed
if (try pending_partial_withdrawals.length() >= preset.PENDING_PARTIAL_WITHDRAWALS_LIMIT and
!is_full_exit_request)
{
return;
}
// bail out if validator is not in beacon state
// note that we don't need to check for 6110 unfinalized vals as they won't be eligible for withdraw/exit anyway
const validator_index = pubkey_to_index.get(withdrawal_request.validator_pubkey) orelse return;
var validators = try state.validators();
if (validator_index >= try validators.length()) return;
const validator = try validators.get(@intCast(validator_index));
if (!(try isValidatorEligibleForWithdrawOrExit(
config,
epoch_cache.epoch,
validator,
&withdrawal_request.source_address,
))) {
return;
}
// TODO Electra: Consider caching pendingPartialWithdrawals
const pending_balance_to_withdraw = try getPendingBalanceToWithdraw(fork, state, validator_index);
var balances = try state.balances();
const validator_balance = try balances.get(@intCast(validator_index));
if (is_full_exit_request) {
// only exit validator if it has no pending withdrawals in the queue
if (pending_balance_to_withdraw == 0) {
try initiateValidatorExit(fork, config, epoch_cache, state, validator);
}
return;
}
// partial withdrawal request
const effective_balance = try validator.get("effective_balance");
const withdrawal_credentials = try validator.getFieldRoot("withdrawal_credentials");
const has_sufficient_effective_balance = effective_balance >= preset.MIN_ACTIVATION_BALANCE;
const has_excess_balance = validator_balance > preset.MIN_ACTIVATION_BALANCE + pending_balance_to_withdraw;
// Only allow partial withdrawals with compounding withdrawal credentials
if (hasCompoundingWithdrawalCredential(withdrawal_credentials) and
has_sufficient_effective_balance and
has_excess_balance)
{
const amount_to_withdraw = @min(validator_balance - preset.MIN_ACTIVATION_BALANCE - pending_balance_to_withdraw, amount);
const exit_queue_epoch = try computeExitEpochAndUpdateChurn(fork, epoch_cache, state, amount_to_withdraw);
const withdrawable_epoch = exit_queue_epoch + config.chain.MIN_VALIDATOR_WITHDRAWABILITY_DELAY;
const pending_partial_withdrawal = PendingPartialWithdrawal{
.validator_index = validator_index,
.amount = amount_to_withdraw,
.withdrawable_epoch = withdrawable_epoch,
};
try pending_partial_withdrawals.pushValue(&pending_partial_withdrawal);
}
}
fn isValidatorEligibleForWithdrawOrExit(
config: *const BeaconConfig,
current_epoch: u64,
validator: *types.phase0.Validator.TreeView,
source_address: []const u8,
) !bool {
const withdrawal_credentials = try validator.getFieldRoot("withdrawal_credentials");
const address = withdrawal_credentials[12..];
const activation_epoch = try validator.get("activation_epoch");
const exit_epoch = try validator.get("exit_epoch");
return (hasExecutionWithdrawalCredential(withdrawal_credentials) and
std.mem.eql(u8, address, source_address) and
(try isActiveValidatorView(validator, current_epoch)) and
exit_epoch == c.FAR_FUTURE_EPOCH and
current_epoch >= activation_epoch + config.chain.SHARD_COMMITTEE_PERIOD);
}