Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions beacon_node/execution_layer/src/engine_api/json_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,8 @@ impl<T: EthSpec> From<ExecutionPayload<T>> for JsonExecutionPayload<T> {
pub struct JsonWithdrawal {
#[serde(with = "eth2_serde_utils::u64_hex_be")]
pub index: u64,
#[serde(with = "eth2_serde_utils::u64_hex_be")]
pub validator_index: u64,
pub address: Address,
#[serde(with = "eth2_serde_utils::u256_hex_be")]
pub amount: Uint256,
Expand All @@ -390,6 +392,7 @@ impl From<Withdrawal> for JsonWithdrawal {
fn from(withdrawal: Withdrawal) -> Self {
Self {
index: withdrawal.index,
validator_index: withdrawal.validator_index,
address: withdrawal.address,
amount: Uint256::from((withdrawal.amount as u128) * 1000000000u128),
}
Expand All @@ -400,6 +403,7 @@ impl From<JsonWithdrawal> for Withdrawal {
fn from(jw: JsonWithdrawal) -> Self {
Self {
index: jw.index,
validator_index: jw.validator_index,
address: jw.address,
//FIXME(sean) if EE gives us too large a number this panics
amount: (jw.amount / 1000000000).as_u64(),
Expand Down
2 changes: 2 additions & 0 deletions consensus/state_processing/src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod get_attesting_indices;
mod get_indexed_attestation;
mod initiate_validator_exit;
mod slash_validator;
mod withdraw_balance;

pub mod altair;
pub mod base;
Expand All @@ -14,6 +15,7 @@ pub use get_attesting_indices::{get_attesting_indices, get_attesting_indices_fro
pub use get_indexed_attestation::get_indexed_attestation;
pub use initiate_validator_exit::initiate_validator_exit;
pub use slash_validator::slash_validator;
pub use withdraw_balance::withdraw_balance;

use safe_arith::SafeArith;
use types::{BeaconState, BeaconStateError, EthSpec};
Expand Down
28 changes: 28 additions & 0 deletions consensus/state_processing/src/common/withdraw_balance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use crate::common::decrease_balance;
use safe_arith::SafeArith;
use types::{BeaconStateError as Error, *};

pub fn withdraw_balance<T: EthSpec>(
state: &mut BeaconState<T>,
validator_index: usize,
amount: u64,
) -> Result<(), Error> {
decrease_balance(state, validator_index as usize, amount)?;

let withdrawal_address = Address::from_slice(
&state
.get_validator(validator_index)?
.withdrawal_credentials
.as_bytes()[12..],
);
let withdrawal = Withdrawal {
index: *state.next_withdrawal_index()?,
validator_index: validator_index as u64,
address: withdrawal_address,
amount,
};
state.next_withdrawal_index_mut()?.safe_add_assign(1)?;
state.withdrawal_queue_mut()?.push(withdrawal)?;

Ok(())
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ pub fn process_epoch<T: EthSpec>(
altair::process_sync_committee_updates(state, spec)?;

// Withdrawals
process_full_withdrawals(state)?;
process_full_withdrawals(state, spec)?;

process_partial_withdrawals(state)?;
process_partial_withdrawals(state, spec)?;

// Rotate the epoch caches to suit the epoch transition.
state.advance_caches(spec)?;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
use crate::common::withdraw_balance;
use crate::EpochProcessingError;
use types::beacon_state::BeaconState;
use types::eth_spec::EthSpec;
use types::{beacon_state::BeaconState, eth_spec::EthSpec, ChainSpec};

pub fn process_full_withdrawals<T: EthSpec>(
_state: &mut BeaconState<T>,
state: &mut BeaconState<T>,
spec: &ChainSpec,
) -> Result<(), EpochProcessingError> {
todo!("implement this");
let current_epoch = state.current_epoch();
// FIXME: is this the most efficient way to do this?
for validator_index in 0..state.validators().len() {
// TODO: is this the correct way to handle validators not existing?
if let (Some(validator), Some(balance)) = (
state.validators().get(validator_index),
state.balances().get(validator_index),
) {
if validator.is_fully_withdrawable_at(*balance, current_epoch, spec) {
withdraw_balance(state, validator_index, *balance)?;
}
}
}
Ok(())
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,39 @@
use crate::common::withdraw_balance;
use crate::EpochProcessingError;
use types::beacon_state::BeaconState;
use types::eth_spec::EthSpec;
use safe_arith::SafeArith;
use types::{beacon_state::BeaconState, eth_spec::EthSpec, ChainSpec};

pub fn process_partial_withdrawals<T: EthSpec>(
_state: &mut BeaconState<T>,
state: &mut BeaconState<T>,
spec: &ChainSpec,
) -> Result<(), EpochProcessingError> {
todo!("implement this");
let mut partial_withdrawals_count = 0;
let mut validator_index = *state.next_partial_withdrawal_validator_index()? as usize;

let n_validators = state.validators().len();
// FIXME: is this the most efficient way to do this?
for _ in 0..n_validators {
// TODO: is this the correct way to handle validators not existing?
if let (Some(validator), Some(balance)) = (
state.validators().get(validator_index),
state.balances().get(validator_index),
) {
if validator.is_partially_withdrawable_validator(*balance, spec) {
withdraw_balance(
state,
validator_index,
*balance - spec.max_effective_balance,
)?;
partial_withdrawals_count.safe_add_assign(1)?;

validator_index = validator_index.safe_add(1)? % n_validators;
if partial_withdrawals_count == T::max_partial_withdrawals_per_epoch() {
break;
}
}
}
}
*state.next_partial_withdrawal_validator_index_mut()? = validator_index as u64;

Ok(())
}
7 changes: 5 additions & 2 deletions consensus/types/src/chain_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ pub struct ChainSpec {
*/
pub genesis_fork_version: [u8; 4],
pub bls_withdrawal_prefix_byte: u8,
pub eth1_address_withdrawal_prefix_byte: u8,

/*
* Time parameters
Expand Down Expand Up @@ -519,7 +520,8 @@ impl ChainSpec {
* Initial Values
*/
genesis_fork_version: [0; 4],
bls_withdrawal_prefix_byte: 0,
bls_withdrawal_prefix_byte: 0x00,
eth1_address_withdrawal_prefix_byte: 0x01,

/*
* Time parameters
Expand Down Expand Up @@ -748,7 +750,8 @@ impl ChainSpec {
* Initial Values
*/
genesis_fork_version: [0x00, 0x00, 0x00, 0x64],
bls_withdrawal_prefix_byte: 0,
bls_withdrawal_prefix_byte: 0x00,
eth1_address_withdrawal_prefix_byte: 0x01,

/*
* Time parameters
Expand Down
21 changes: 21 additions & 0 deletions consensus/types/src/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,27 @@ impl Validator {
// Has not yet been activated
&& self.activation_epoch == spec.far_future_epoch
}

/// Returns `true` if the validator has eth1 withdrawal credential
pub fn has_eth1_withdrawal_credential(&self, spec: &ChainSpec) -> bool {
self.withdrawal_credentials
.as_bytes()
.first()
.map(|byte| *byte == spec.eth1_address_withdrawal_prefix_byte)
.unwrap_or(false)
}

/// Returns `true` if the validator is fully withdrawable at some epoch
pub fn is_fully_withdrawable_at(&self, balance: u64, epoch: Epoch, spec: &ChainSpec) -> bool {
self.has_eth1_withdrawal_credential(spec) && self.withdrawable_epoch <= epoch && balance > 0
}

/// Returns `true` if the validator is partially withdrawable
pub fn is_partially_withdrawable_validator(&self, balance: u64, spec: &ChainSpec) -> bool {
self.has_eth1_withdrawal_credential(spec)
&& self.effective_balance == spec.max_effective_balance
&& balance > spec.max_effective_balance
}
}

impl Default for Validator {
Expand Down
1 change: 1 addition & 0 deletions consensus/types/src/withdrawal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use tree_hash_derive::TreeHash;
pub struct Withdrawal {
#[serde(with = "eth2_serde_utils::quoted_u64")]
pub index: u64,
pub validator_index: u64,
pub address: Address,
pub amount: u64,
}
Expand Down