Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(capella): implement process_withdrawals #252

Merged
merged 11 commits into from
Sep 21, 2023
47 changes: 38 additions & 9 deletions ethereum-consensus/src/capella/block_processing.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::{
capella::{
compute_timestamp_at_slot, get_current_epoch, get_randao_mix, process_attestation,
process_attester_slashing, process_block_header, process_deposit, process_eth1_data,
process_proposer_slashing, process_randao, process_sync_aggregate, process_voluntary_exit,
BeaconBlock, BeaconBlockBody, BeaconState, ExecutionEngine, ExecutionPayload,
ExecutionPayloadHeader, NewPayloadRequest, SignedBlsToExecutionChange,
compute_timestamp_at_slot, decrease_balance, get_current_epoch, get_expected_withdrawals,
get_randao_mix, process_attestation, process_attester_slashing, process_block_header,
process_deposit, process_eth1_data, process_proposer_slashing, process_randao,
process_sync_aggregate, process_voluntary_exit, BeaconBlock, BeaconBlockBody, BeaconState,
ExecutionEngine, ExecutionPayload, ExecutionPayloadHeader, NewPayloadRequest,
SignedBlsToExecutionChange,
},
state_transition::{
invalid_operation_error, Context, InvalidDeposit, InvalidExecutionPayload,
Expand All @@ -13,6 +14,8 @@ use crate::{
};
use ssz_rs::prelude::*;

use super::mainnet::MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP;
ralexstokes marked this conversation as resolved.
Show resolved Hide resolved
ralexstokes marked this conversation as resolved.
Show resolved Hide resolved

pub fn process_bls_to_execution_change<
const SLOTS_PER_HISTORICAL_ROOT: usize,
const HISTORICAL_ROOTS_LIMIT: usize,
Expand Down Expand Up @@ -240,7 +243,7 @@ pub fn process_withdrawals<
const MAX_TRANSACTIONS_PER_PAYLOAD: usize,
const MAX_WITHDRAWALS_PER_PAYLOAD: usize,
>(
_state: &mut BeaconState<
state: &mut BeaconState<
SLOTS_PER_HISTORICAL_ROOT,
HISTORICAL_ROOTS_LIMIT,
ETH1_DATA_VOTES_BOUND,
Expand All @@ -252,16 +255,42 @@ pub fn process_withdrawals<
BYTES_PER_LOGS_BLOOM,
MAX_EXTRA_DATA_BYTES,
>,
_execution_payload: &ExecutionPayload<
execution_payload: &ExecutionPayload<
BYTES_PER_LOGS_BLOOM,
MAX_EXTRA_DATA_BYTES,
MAX_BYTES_PER_TRANSACTION,
MAX_TRANSACTIONS_PER_PAYLOAD,
MAX_WITHDRAWALS_PER_PAYLOAD,
>,
_context: &Context,
context: &Context,
) -> Result<()> {
unimplemented!()
let expected_withdrawals = get_expected_withdrawals(state, context).unwrap();
ralexstokes marked this conversation as resolved.
Show resolved Hide resolved
assert_eq!(execution_payload.withdrawals.len(), expected_withdrawals.len());
for (expected_withdrawal, withdrawal) in
expected_withdrawals.iter().zip(execution_payload.withdrawals.iter())
{
assert_eq!(withdrawal, expected_withdrawal);
decrease_balance(state, withdrawal.validator_index, withdrawal.amount);
}
ralexstokes marked this conversation as resolved.
Show resolved Hide resolved

// Update the next withdrawal index if this block contained withdrawals
if let Some(latest_withdrawal) = expected_withdrawals.last() {
state.next_withdrawal_index = latest_withdrawal.index + 1;
}

// Update the next validator index to start the next withdrawal sweep
if expected_withdrawals.len() == MAX_WITHDRAWALS_PER_PAYLOAD {
ralexstokes marked this conversation as resolved.
Show resolved Hide resolved
// Next sweep starts after the latest withdrawal's validator index
let latest_withdrawal = expected_withdrawals.last().unwrap();
ralexstokes marked this conversation as resolved.
Show resolved Hide resolved
let next_validator_index = (latest_withdrawal.validator_index + 1) % state.validators.len();
state.next_withdrawal_validator_index = next_validator_index;
} else {
// Advance sweep by the max length of the sweep if there was not a full set of withdrawals
let next_index =
state.next_withdrawal_validator_index + MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP;
ralexstokes marked this conversation as resolved.
Show resolved Hide resolved
state.next_withdrawal_validator_index = next_index & state.validators.len();
ralexstokes marked this conversation as resolved.
Show resolved Hide resolved
}
Ok(())
}

pub fn process_block<
Expand Down
70 changes: 69 additions & 1 deletion ethereum-consensus/src/capella/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,13 @@ use crate::{
eth_aggregate_public_keys, eth_fast_aggregate_verify, fast_aggregate_verify, hash,
verify_signature,
},
phase0::mainnet::MAX_EFFECTIVE_BALANCE,
ssz::*,
};
use integer_sqrt::IntegerSquareRoot;
use ssz_rs::prelude::*;
use std::{
cmp,
cmp::{self, min},
collections::{HashMap, HashSet},
iter::zip,
mem,
Expand Down Expand Up @@ -3312,3 +3313,70 @@ pub fn state_transition<
state_transition_block_in_slot(state, signed_block, execution_engine, validation, context)
}
pub use crate::capella::execution_engine::ExecutionEngine;

ralexstokes marked this conversation as resolved.
Show resolved Hide resolved
use super::mainnet::{MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP, MAX_WITHDRAWALS_PER_PAYLOAD};

pub fn get_expected_withdrawals<
const SLOTS_PER_HISTORICAL_ROOT: usize,
const HISTORICAL_ROOTS_LIMIT: usize,
const ETH1_DATA_VOTES_BOUND: usize,
const VALIDATOR_REGISTRY_LIMIT: usize,
const EPOCHS_PER_HISTORICAL_VECTOR: usize,
const EPOCHS_PER_SLASHINGS_VECTOR: usize,
const MAX_VALIDATORS_PER_COMMITTEE: usize,
const SYNC_COMMITTEE_SIZE: usize,
const BYTES_PER_LOGS_BLOOM: usize,
const MAX_EXTRA_DATA_BYTES: usize,
>(
state: &BeaconState<
SLOTS_PER_HISTORICAL_ROOT,
HISTORICAL_ROOTS_LIMIT,
ETH1_DATA_VOTES_BOUND,
VALIDATOR_REGISTRY_LIMIT,
EPOCHS_PER_HISTORICAL_VECTOR,
EPOCHS_PER_SLASHINGS_VECTOR,
MAX_VALIDATORS_PER_COMMITTEE,
SYNC_COMMITTEE_SIZE,
BYTES_PER_LOGS_BLOOM,
MAX_EXTRA_DATA_BYTES,
>,
context: &Context,
) -> Result<Vec<Withdrawal>> {
ralexstokes marked this conversation as resolved.
Show resolved Hide resolved
let epoch = get_current_epoch(state, context);
let mut withdrawal_index = state.next_withdrawal_index;
let mut validator_index = state.next_withdrawal_validator_index;
let mut withdrawals = Vec::new();
ralexstokes marked this conversation as resolved.
Show resolved Hide resolved
let bound = min(state.validators.len(), MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP);
ralexstokes marked this conversation as resolved.
Show resolved Hide resolved
for _ in 0..bound {
let validator = &state.validators[validator_index];
let balance = state.balances[validator_index];
if is_fully_withdrawable_validator(validator, balance, epoch) {
let address =
ExecutionAddress::try_from(&validator.withdrawal_credentials.as_slice()[12..])
.unwrap();
ralexstokes marked this conversation as resolved.
Show resolved Hide resolved
withdrawals.push(Withdrawal {
index: withdrawal_index,
validator_index,
address,
amount: balance,
});
withdrawal_index += 1;
} else if is_partially_withdrawable_validator(validator, balance, context) {
let address =
ExecutionAddress::try_from(&validator.withdrawal_credentials.as_slice()[12..])
.unwrap();
ralexstokes marked this conversation as resolved.
Show resolved Hide resolved
withdrawals.push(Withdrawal {
index: withdrawal_index,
validator_index,
address,
amount: balance - MAX_EFFECTIVE_BALANCE,
ralexstokes marked this conversation as resolved.
Show resolved Hide resolved
});
withdrawal_index += 1;
}
if withdrawals.len() == MAX_WITHDRAWALS_PER_PAYLOAD {
ralexstokes marked this conversation as resolved.
Show resolved Hide resolved
break
}
validator_index = (validator_index + 1) % state.validators.len();
}
Ok(withdrawals)
}