Skip to content

Commit 1bb1c8f

Browse files
committed
Add initial participation cache
1 parent 8fa6e46 commit 1bb1c8f

File tree

5 files changed

+234
-13
lines changed

5 files changed

+234
-13
lines changed

consensus/state_processing/src/per_epoch_processing.rs

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ use errors::EpochProcessingError as Error;
66
pub use registry_updates::process_registry_updates;
77
use safe_arith::SafeArith;
88
pub use slashings::process_slashings;
9-
use types::{BeaconState, ChainSpec, EthSpec};
9+
use types::{
10+
consts::altair::{TIMELY_HEAD_FLAG_INDEX, TIMELY_SOURCE_FLAG_INDEX, TIMELY_TARGET_FLAG_INDEX},
11+
BeaconState, BeaconStateError, ChainSpec, EthSpec,
12+
};
1013
pub use weigh_justification_and_finalization::weigh_justification_and_finalization;
1114

1215
pub mod altair;
@@ -25,7 +28,90 @@ pub mod weigh_justification_and_finalization;
2528
#[derive(PartialEq, Debug)]
2629
pub struct EpochProcessingSummary {
2730
pub total_balances: TotalBalances,
28-
pub statuses: Vec<ValidatorStatus>,
31+
pub statuses: Vec<ValidatorParticipation>,
32+
}
33+
34+
#[derive(PartialEq, Debug)]
35+
pub struct ValidatorParticipation {
36+
/*
37+
* Current Epoch
38+
*/
39+
pub is_current_epoch_timely_head_attester: bool,
40+
pub is_current_epoch_timely_source_attester: bool,
41+
pub is_current_epoch_timely_target_attester: bool,
42+
/*
43+
* Previous Epoch
44+
*/
45+
pub is_previous_epoch_timely_head_attester: bool,
46+
pub is_previous_epoch_timely_source_attester: bool,
47+
pub is_previous_epoch_timely_target_attester: bool,
48+
pub previous_epoch_inclusion_delay: Option<u64>,
49+
}
50+
51+
impl ValidatorParticipation {
52+
pub fn altair<T: EthSpec>(state: &BeaconState<T>) -> Result<Vec<Self>, BeaconStateError> {
53+
let state = state.as_altair()?;
54+
55+
let mut participations = Vec::with_capacity(state.validators.len());
56+
for i in 0..state.validators.len() {
57+
let current = state
58+
.current_epoch_participation
59+
.get(i)
60+
.ok_or(BeaconStateError::UnknownValidator(i))?;
61+
let previous = state
62+
.previous_epoch_participation
63+
.get(i)
64+
.ok_or(BeaconStateError::UnknownValidator(i))?;
65+
66+
participations.push(ValidatorParticipation {
67+
/*
68+
* Current Epoch
69+
*/
70+
is_current_epoch_timely_head_attester: current.has_flag(TIMELY_HEAD_FLAG_INDEX)?,
71+
is_current_epoch_timely_source_attester: current
72+
.has_flag(TIMELY_SOURCE_FLAG_INDEX)?,
73+
is_current_epoch_timely_target_attester: current
74+
.has_flag(TIMELY_TARGET_FLAG_INDEX)?,
75+
/*
76+
* Previous Epoch
77+
*/
78+
is_previous_epoch_timely_head_attester: previous
79+
.has_flag(TIMELY_HEAD_FLAG_INDEX)?,
80+
is_previous_epoch_timely_source_attester: previous
81+
.has_flag(TIMELY_SOURCE_FLAG_INDEX)?,
82+
is_previous_epoch_timely_target_attester: previous
83+
.has_flag(TIMELY_TARGET_FLAG_INDEX)?,
84+
// Field is not relevant for Altair states.
85+
previous_epoch_inclusion_delay: None,
86+
})
87+
}
88+
89+
Ok(participations)
90+
}
91+
92+
pub fn base(validator_statuses: &[ValidatorStatus]) -> Vec<Self> {
93+
validator_statuses
94+
.iter()
95+
.map(|v| {
96+
Self {
97+
/*
98+
* Current Epoch
99+
*/
100+
// FIXME(paul): avoid making this always `false`.
101+
is_current_epoch_timely_head_attester: false,
102+
is_current_epoch_timely_source_attester: v.is_current_epoch_attester,
103+
is_current_epoch_timely_target_attester: v.is_current_epoch_target_attester,
104+
/*
105+
* Previous Epoch
106+
*/
107+
is_previous_epoch_timely_head_attester: v.is_previous_epoch_head_attester,
108+
is_previous_epoch_timely_source_attester: v.is_previous_epoch_attester,
109+
is_previous_epoch_timely_target_attester: v.is_previous_epoch_target_attester,
110+
previous_epoch_inclusion_delay: v.inclusion_info.map(|i| i.delay),
111+
}
112+
})
113+
.collect()
114+
}
29115
}
30116

31117
/// Performs per-epoch processing on some BeaconState.

consensus/state_processing/src/per_epoch_processing/altair.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,19 @@ use crate::per_epoch_processing::{
44
historical_roots_update::process_historical_roots_update,
55
resets::{process_eth1_data_reset, process_randao_mixes_reset, process_slashings_reset},
66
validator_statuses::ValidatorStatuses,
7+
ValidatorParticipation,
78
};
89
pub use inactivity_updates::process_inactivity_updates;
910
pub use justification_and_finalization::process_justification_and_finalization;
11+
pub use participation_cache::ParticipationCache;
1012
pub use participation_flag_updates::process_participation_flag_updates;
1113
pub use rewards_and_penalties::process_rewards_and_penalties;
1214
pub use sync_committee_updates::process_sync_committee_updates;
1315
use types::{BeaconState, ChainSpec, EthSpec, RelativeEpoch};
1416

1517
pub mod inactivity_updates;
1618
pub mod justification_and_finalization;
19+
pub mod participation_cache;
1720
pub mod participation_flag_updates;
1821
pub mod rewards_and_penalties;
1922
pub mod sync_committee_updates;
@@ -27,8 +30,10 @@ pub fn process_epoch<T: EthSpec>(
2730
state.build_committee_cache(RelativeEpoch::Current, spec)?;
2831
state.build_committee_cache(RelativeEpoch::Next, spec)?;
2932

33+
let participation_cache = ParticipationCache::altair(state, spec)?;
34+
3035
// Justification and finalization.
31-
process_justification_and_finalization(state, spec)?;
36+
process_justification_and_finalization(state, &participation_cache, spec)?;
3237

3338
process_inactivity_updates(state, spec)?;
3439

@@ -77,6 +82,6 @@ pub fn process_epoch<T: EthSpec>(
7782

7883
Ok(EpochProcessingSummary {
7984
total_balances: validator_statuses.total_balances,
80-
statuses: validator_statuses.statuses,
85+
statuses: ValidatorParticipation::altair(state)?,
8186
})
8287
}

consensus/state_processing/src/per_epoch_processing/altair/justification_and_finalization.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use crate::per_epoch_processing::weigh_justification_and_finalization;
2-
use crate::per_epoch_processing::Error;
2+
use crate::per_epoch_processing::{altair::ParticipationCache, Error};
33
use safe_arith::SafeArith;
44
use types::consts::altair::TIMELY_TARGET_FLAG_INDEX;
55
use types::{BeaconState, ChainSpec, EthSpec};
66

77
/// Update the justified and finalized checkpoints for matching target attestations.
88
pub fn process_justification_and_finalization<T: EthSpec>(
99
state: &mut BeaconState<T>,
10+
participation_cache: &ParticipationCache,
1011
spec: &ChainSpec,
1112
) -> Result<(), Error> {
1213
if state.current_epoch() <= T::genesis_epoch().safe_add(1)? {
@@ -15,13 +16,10 @@ pub fn process_justification_and_finalization<T: EthSpec>(
1516

1617
let previous_epoch = state.previous_epoch();
1718
let current_epoch = state.current_epoch();
18-
let previous_indices = state.get_unslashed_participating_indices(
19-
TIMELY_TARGET_FLAG_INDEX,
20-
previous_epoch,
21-
spec,
22-
)?;
23-
let current_indices =
24-
state.get_unslashed_participating_indices(TIMELY_TARGET_FLAG_INDEX, current_epoch, spec)?;
19+
let previous_indices = participation_cache
20+
.get_unslashed_participating_indices(TIMELY_TARGET_FLAG_INDEX, previous_epoch)?;
21+
let current_indices = participation_cache
22+
.get_unslashed_participating_indices(TIMELY_TARGET_FLAG_INDEX, current_epoch)?;
2523
let total_active_balance = state.get_total_balance(
2624
state
2725
.get_active_validator_indices(current_epoch, spec)?
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
use safe_arith::ArithError;
2+
use std::collections::HashMap;
3+
use types::{
4+
consts::altair::NUM_FLAG_INDICES, BeaconState, BeaconStateError, ChainSpec, Epoch, EthSpec,
5+
ParticipationFlags,
6+
};
7+
8+
pub struct ParticipationCache {
9+
current_epoch: Epoch,
10+
current_epoch_map: HashMap<usize, ParticipationFlags>,
11+
previous_epoch: Epoch,
12+
previous_epoch_map: HashMap<usize, ParticipationFlags>,
13+
}
14+
15+
impl ParticipationCache {
16+
pub fn altair<T: EthSpec>(
17+
state: &BeaconState<T>,
18+
spec: &ChainSpec,
19+
) -> Result<Self, BeaconStateError> {
20+
let current_epoch = state.current_epoch();
21+
let previous_epoch = state.previous_epoch();
22+
23+
Ok(Self {
24+
current_epoch,
25+
current_epoch_map: get_epoch_participation(state, current_epoch, spec)?,
26+
previous_epoch,
27+
previous_epoch_map: get_epoch_participation(state, previous_epoch, spec)?,
28+
})
29+
}
30+
31+
pub fn get_unslashed_participating_indices(
32+
&self,
33+
flag_index: usize,
34+
epoch: Epoch,
35+
) -> Result<UnslashedParticipatingIndices, BeaconStateError> {
36+
let map = if epoch == self.current_epoch {
37+
&self.current_epoch_map
38+
} else if epoch == self.previous_epoch {
39+
&self.previous_epoch_map
40+
} else {
41+
return Err(BeaconStateError::EpochOutOfBounds);
42+
};
43+
44+
// Note: protects the iterator.
45+
if flag_index >= NUM_FLAG_INDICES {
46+
return Err(ArithError::Overflow.into());
47+
}
48+
49+
Ok(UnslashedParticipatingIndices { map, flag_index })
50+
}
51+
}
52+
53+
pub struct UnslashedParticipatingIndices<'a> {
54+
map: &'a HashMap<usize, ParticipationFlags>,
55+
flag_index: usize,
56+
}
57+
58+
impl<'a> UnslashedParticipatingIndices<'a> {
59+
pub fn contains(&self, val_index: usize) -> Result<bool, ArithError> {
60+
self.map
61+
.get(&val_index)
62+
.map(|participation_flags| participation_flags.has_flag(self.flag_index))
63+
.unwrap_or(Ok(false))
64+
}
65+
}
66+
67+
pub struct UnslashedParticipatingIndicesIter<'a> {
68+
iter: std::collections::hash_map::Iter<'a, usize, ParticipationFlags>,
69+
flag_index: usize,
70+
}
71+
72+
impl<'a> Iterator for UnslashedParticipatingIndicesIter<'a> {
73+
type Item = &'a usize;
74+
75+
fn next(&mut self) -> Option<Self::Item> {
76+
while let Some((val_index, participation_flags)) = self.iter.next() {
77+
if participation_flags
78+
.has_flag(self.flag_index)
79+
.unwrap_or(false)
80+
{
81+
return Some(val_index);
82+
}
83+
}
84+
85+
None
86+
}
87+
}
88+
89+
impl<'a> IntoIterator for &'a UnslashedParticipatingIndices<'a> {
90+
type Item = &'a usize;
91+
type IntoIter = UnslashedParticipatingIndicesIter<'a>;
92+
93+
fn into_iter(self) -> Self::IntoIter {
94+
// todo: check flag is legit.
95+
UnslashedParticipatingIndicesIter {
96+
iter: self.map.iter(),
97+
flag_index: self.flag_index,
98+
}
99+
}
100+
}
101+
102+
fn get_epoch_participation<T: EthSpec>(
103+
state: &BeaconState<T>,
104+
epoch: Epoch,
105+
spec: &ChainSpec,
106+
) -> Result<HashMap<usize, ParticipationFlags>, BeaconStateError> {
107+
let epoch_participation = if epoch == state.current_epoch() {
108+
state.current_epoch_participation()?
109+
} else if epoch == state.previous_epoch() {
110+
state.previous_epoch_participation()?
111+
} else {
112+
return Err(BeaconStateError::EpochOutOfBounds);
113+
};
114+
115+
// Might be too large due to slashed boiz.
116+
let active_validator_indices = state.get_active_validator_indices(epoch, spec)?;
117+
let mut map = HashMap::with_capacity(active_validator_indices.len());
118+
119+
for val_index in active_validator_indices {
120+
if !state.get_validator(val_index)?.slashed {
121+
map.insert(
122+
val_index,
123+
*epoch_participation
124+
.get(val_index)
125+
.ok_or(BeaconStateError::ParticipationOutOfBounds(val_index))?,
126+
);
127+
}
128+
}
129+
130+
Ok(map)
131+
}

consensus/state_processing/src/per_epoch_processing/base.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::per_epoch_processing::{
66
effective_balance_updates::process_effective_balance_updates,
77
historical_roots_update::process_historical_roots_update,
88
resets::{process_eth1_data_reset, process_randao_mixes_reset, process_slashings_reset},
9+
ValidatorParticipation,
910
};
1011
pub use justification_and_finalization::process_justification_and_finalization;
1112
pub use participation_record_updates::process_participation_record_updates;
@@ -71,6 +72,6 @@ pub fn process_epoch<T: EthSpec>(
7172

7273
Ok(EpochProcessingSummary {
7374
total_balances: validator_statuses.total_balances,
74-
statuses: validator_statuses.statuses,
75+
statuses: ValidatorParticipation::base(&validator_statuses.statuses),
7576
})
7677
}

0 commit comments

Comments
 (0)