Skip to content

Commit f1a3b3b

Browse files
authored
Added Capella Epoch Processing Logic (#3666)
1 parent 137f230 commit f1a3b3b

File tree

9 files changed

+113
-12
lines changed

9 files changed

+113
-12
lines changed

beacon_node/execution_layer/src/engine_api/json_structures.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,8 @@ impl<T: EthSpec> From<ExecutionPayload<T>> for JsonExecutionPayload<T> {
381381
pub struct JsonWithdrawal {
382382
#[serde(with = "eth2_serde_utils::u64_hex_be")]
383383
pub index: u64,
384+
#[serde(with = "eth2_serde_utils::u64_hex_be")]
385+
pub validator_index: u64,
384386
pub address: Address,
385387
#[serde(with = "eth2_serde_utils::u256_hex_be")]
386388
pub amount: Uint256,
@@ -390,6 +392,7 @@ impl From<Withdrawal> for JsonWithdrawal {
390392
fn from(withdrawal: Withdrawal) -> Self {
391393
Self {
392394
index: withdrawal.index,
395+
validator_index: withdrawal.validator_index,
393396
address: withdrawal.address,
394397
amount: Uint256::from((withdrawal.amount as u128) * 1000000000u128),
395398
}
@@ -400,6 +403,7 @@ impl From<JsonWithdrawal> for Withdrawal {
400403
fn from(jw: JsonWithdrawal) -> Self {
401404
Self {
402405
index: jw.index,
406+
validator_index: jw.validator_index,
403407
address: jw.address,
404408
//FIXME(sean) if EE gives us too large a number this panics
405409
amount: (jw.amount / 1000000000).as_u64(),

consensus/state_processing/src/common/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod get_attesting_indices;
44
mod get_indexed_attestation;
55
mod initiate_validator_exit;
66
mod slash_validator;
7+
mod withdraw_balance;
78

89
pub mod altair;
910
pub mod base;
@@ -14,6 +15,7 @@ pub use get_attesting_indices::{get_attesting_indices, get_attesting_indices_fro
1415
pub use get_indexed_attestation::get_indexed_attestation;
1516
pub use initiate_validator_exit::initiate_validator_exit;
1617
pub use slash_validator::slash_validator;
18+
pub use withdraw_balance::withdraw_balance;
1719

1820
use safe_arith::SafeArith;
1921
use types::{BeaconState, BeaconStateError, EthSpec};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use crate::common::decrease_balance;
2+
use safe_arith::SafeArith;
3+
use types::{BeaconStateError as Error, *};
4+
5+
pub fn withdraw_balance<T: EthSpec>(
6+
state: &mut BeaconState<T>,
7+
validator_index: usize,
8+
amount: u64,
9+
) -> Result<(), Error> {
10+
decrease_balance(state, validator_index as usize, amount)?;
11+
12+
let withdrawal_address = Address::from_slice(
13+
&state
14+
.get_validator(validator_index)?
15+
.withdrawal_credentials
16+
.as_bytes()[12..],
17+
);
18+
let withdrawal = Withdrawal {
19+
index: *state.next_withdrawal_index()?,
20+
validator_index: validator_index as u64,
21+
address: withdrawal_address,
22+
amount,
23+
};
24+
state.next_withdrawal_index_mut()?.safe_add_assign(1)?;
25+
state.withdrawal_queue_mut()?.push(withdrawal)?;
26+
27+
Ok(())
28+
}

consensus/state_processing/src/per_epoch_processing/capella.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ pub fn process_epoch<T: EthSpec>(
6666
altair::process_sync_committee_updates(state, spec)?;
6767

6868
// Withdrawals
69-
process_full_withdrawals(state)?;
69+
process_full_withdrawals(state, spec)?;
7070

71-
process_partial_withdrawals(state)?;
71+
process_partial_withdrawals(state, spec)?;
7272

7373
// Rotate the epoch caches to suit the epoch transition.
7474
state.advance_caches(spec)?;
Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
1+
use crate::common::withdraw_balance;
12
use crate::EpochProcessingError;
2-
use types::beacon_state::BeaconState;
3-
use types::eth_spec::EthSpec;
3+
use types::{beacon_state::BeaconState, eth_spec::EthSpec, ChainSpec};
44

55
pub fn process_full_withdrawals<T: EthSpec>(
6-
_state: &mut BeaconState<T>,
6+
state: &mut BeaconState<T>,
7+
spec: &ChainSpec,
78
) -> Result<(), EpochProcessingError> {
8-
todo!("implement this");
9+
let current_epoch = state.current_epoch();
10+
// FIXME: is this the most efficient way to do this?
11+
for validator_index in 0..state.validators().len() {
12+
// TODO: is this the correct way to handle validators not existing?
13+
if let (Some(validator), Some(balance)) = (
14+
state.validators().get(validator_index),
15+
state.balances().get(validator_index),
16+
) {
17+
if validator.is_fully_withdrawable_at(*balance, current_epoch, spec) {
18+
withdraw_balance(state, validator_index, *balance)?;
19+
}
20+
}
21+
}
922
Ok(())
1023
}
Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,39 @@
1+
use crate::common::withdraw_balance;
12
use crate::EpochProcessingError;
2-
use types::beacon_state::BeaconState;
3-
use types::eth_spec::EthSpec;
3+
use safe_arith::SafeArith;
4+
use types::{beacon_state::BeaconState, eth_spec::EthSpec, ChainSpec};
45

56
pub fn process_partial_withdrawals<T: EthSpec>(
6-
_state: &mut BeaconState<T>,
7+
state: &mut BeaconState<T>,
8+
spec: &ChainSpec,
79
) -> Result<(), EpochProcessingError> {
8-
todo!("implement this");
10+
let mut partial_withdrawals_count = 0;
11+
let mut validator_index = *state.next_partial_withdrawal_validator_index()? as usize;
12+
13+
let n_validators = state.validators().len();
14+
// FIXME: is this the most efficient way to do this?
15+
for _ in 0..n_validators {
16+
// TODO: is this the correct way to handle validators not existing?
17+
if let (Some(validator), Some(balance)) = (
18+
state.validators().get(validator_index),
19+
state.balances().get(validator_index),
20+
) {
21+
if validator.is_partially_withdrawable_validator(*balance, spec) {
22+
withdraw_balance(
23+
state,
24+
validator_index,
25+
*balance - spec.max_effective_balance,
26+
)?;
27+
partial_withdrawals_count.safe_add_assign(1)?;
28+
29+
validator_index = validator_index.safe_add(1)? % n_validators;
30+
if partial_withdrawals_count == T::max_partial_withdrawals_per_epoch() {
31+
break;
32+
}
33+
}
34+
}
35+
}
36+
*state.next_partial_withdrawal_validator_index_mut()? = validator_index as u64;
37+
938
Ok(())
1039
}

consensus/types/src/chain_spec.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ pub struct ChainSpec {
7373
*/
7474
pub genesis_fork_version: [u8; 4],
7575
pub bls_withdrawal_prefix_byte: u8,
76+
pub eth1_address_withdrawal_prefix_byte: u8,
7677

7778
/*
7879
* Time parameters
@@ -519,7 +520,8 @@ impl ChainSpec {
519520
* Initial Values
520521
*/
521522
genesis_fork_version: [0; 4],
522-
bls_withdrawal_prefix_byte: 0,
523+
bls_withdrawal_prefix_byte: 0x00,
524+
eth1_address_withdrawal_prefix_byte: 0x01,
523525

524526
/*
525527
* Time parameters
@@ -748,7 +750,8 @@ impl ChainSpec {
748750
* Initial Values
749751
*/
750752
genesis_fork_version: [0x00, 0x00, 0x00, 0x64],
751-
bls_withdrawal_prefix_byte: 0,
753+
bls_withdrawal_prefix_byte: 0x00,
754+
eth1_address_withdrawal_prefix_byte: 0x01,
752755

753756
/*
754757
* Time parameters

consensus/types/src/validator.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,27 @@ impl Validator {
6565
// Has not yet been activated
6666
&& self.activation_epoch == spec.far_future_epoch
6767
}
68+
69+
/// Returns `true` if the validator has eth1 withdrawal credential
70+
pub fn has_eth1_withdrawal_credential(&self, spec: &ChainSpec) -> bool {
71+
self.withdrawal_credentials
72+
.as_bytes()
73+
.first()
74+
.map(|byte| *byte == spec.eth1_address_withdrawal_prefix_byte)
75+
.unwrap_or(false)
76+
}
77+
78+
/// Returns `true` if the validator is fully withdrawable at some epoch
79+
pub fn is_fully_withdrawable_at(&self, balance: u64, epoch: Epoch, spec: &ChainSpec) -> bool {
80+
self.has_eth1_withdrawal_credential(spec) && self.withdrawable_epoch <= epoch && balance > 0
81+
}
82+
83+
/// Returns `true` if the validator is partially withdrawable
84+
pub fn is_partially_withdrawable_validator(&self, balance: u64, spec: &ChainSpec) -> bool {
85+
self.has_eth1_withdrawal_credential(spec)
86+
&& self.effective_balance == spec.max_effective_balance
87+
&& balance > spec.max_effective_balance
88+
}
6889
}
6990

7091
impl Default for Validator {

consensus/types/src/withdrawal.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use tree_hash_derive::TreeHash;
1515
pub struct Withdrawal {
1616
#[serde(with = "eth2_serde_utils::quoted_u64")]
1717
pub index: u64,
18+
pub validator_index: u64,
1819
pub address: Address,
1920
pub amount: u64,
2021
}

0 commit comments

Comments
 (0)