Skip to content

Commit fef3e16

Browse files
committed
Fix progressive balances mismatch. Perform checks only in debug mode. Some cleanups.
1 parent f7cf5dd commit fef3e16

File tree

11 files changed

+143
-125
lines changed

11 files changed

+143
-125
lines changed

consensus/fork_choice/src/fork_choice.rs

Lines changed: 57 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,14 @@ use std::cmp::Ordering;
1313
use std::collections::BTreeSet;
1414
use std::marker::PhantomData;
1515
use std::time::Duration;
16+
use types::ProgressiveTotalBalances;
1617
use types::{
1718
consts::merge::INTERVALS_PER_SLOT, AbstractExecPayload, AttestationShufflingId,
1819
AttesterSlashing, BeaconBlockRef, BeaconState, BeaconStateError, ChainSpec, Checkpoint, Epoch,
1920
EthSpec, ExecPayload, ExecutionBlockHash, Hash256, IndexedAttestation, RelativeEpoch,
2021
SignedBeaconBlock, Slot,
2122
};
2223

23-
#[cfg(test)]
24-
use state_processing::per_epoch_processing::{
25-
weigh_justification_and_finalization, JustificationAndFinalizationState,
26-
};
27-
#[cfg(test)]
28-
use types::ProgressiveTotalBalances;
29-
3024
#[derive(Debug)]
3125
pub enum Error<T> {
3226
InvalidAttestation(InvalidAttestation),
@@ -768,21 +762,17 @@ where
768762
| BeaconBlockRef::Altair(_) => {
769763
let participation_cache = ParticipationCache::new(state, spec)
770764
.map_err(Error::ParticipationCacheBuild)?;
771-
let processing_result =
772-
per_epoch_processing::altair::process_justification_and_finalization(
773-
state,
774-
&participation_cache,
775-
)?;
776765

777-
#[cfg(test)]
778-
Self::check_processing_results_with_progressive_cache(
779-
state,
780-
&processing_result,
766+
check_progressive_total_balances(
767+
state.progressive_total_balances(),
781768
&participation_cache,
769+
spec,
782770
);
783771

784-
#[allow(clippy::let_and_return)]
785-
processing_result
772+
per_epoch_processing::altair::process_justification_and_finalization(
773+
state,
774+
&participation_cache,
775+
)?
786776
}
787777
BeaconBlockRef::Base(_) => {
788778
let mut validator_statuses =
@@ -1536,59 +1526,61 @@ where
15361526
queued_attestations: self.queued_attestations().to_vec(),
15371527
}
15381528
}
1529+
}
15391530

1540-
#[cfg(test)]
1541-
fn check_processing_results_with_progressive_cache(
1542-
state: &BeaconState<E>,
1543-
processing_results: &JustificationAndFinalizationState<E>,
1544-
participation_cache: &ParticipationCache,
1545-
) {
1546-
let expected_justified_checkpoint = processing_results.current_justified_checkpoint();
1547-
let expected_finalized_checkpoint = processing_results.finalized_checkpoint();
1548-
let expected_previous_target_balance = participation_cache
1531+
/// Check the `ProgressiveTotalBalances` values against the computed `ParticipationCache`.
1532+
///
1533+
/// ## Panics
1534+
///
1535+
/// In debug mode (`debug_assertions` set to `true`), this function panics if the cached progressive
1536+
/// balances doesn't match computed values from `ParticipationCache`, or if it fails to perform the
1537+
/// check.
1538+
fn check_progressive_total_balances(
1539+
progressive_total_balances: &ProgressiveTotalBalances,
1540+
participation_cache: &ParticipationCache,
1541+
spec: &ChainSpec,
1542+
) {
1543+
let do_check = || {
1544+
// FIXME[JC]: Converts value to 0 if it is the same as `EFFECTIVE_BALANCE_INCREMENT`.
1545+
// `ParticipationCache` methods return `EFFECTIVE_BALANCE_INCREMENT` (1,000,000,000)
1546+
// when the balance is 0, and this breaks our calculation.
1547+
let handle_zero_effective_balance = |val| {
1548+
if val == spec.effective_balance_increment {
1549+
0
1550+
} else {
1551+
val
1552+
}
1553+
};
1554+
1555+
let previous_target_balance = participation_cache
15491556
.previous_epoch_target_attesting_balance()
1550-
.unwrap();
1551-
let expected_current_target_balance = participation_cache
1557+
.map_err(|e| BeaconStateError::ParticipationCacheError(format!("{:?}", e)))?;
1558+
let cached_previous_target_balance =
1559+
progressive_total_balances.previous_epoch_target_attesting_balance()?;
1560+
debug_assert!(
1561+
handle_zero_effective_balance(previous_target_balance) == cached_previous_target_balance,
1562+
"Previous epoch target attesting balance from progressive cache does not match computed value."
1563+
);
1564+
1565+
let current_target_balance = participation_cache
15521566
.current_epoch_target_attesting_balance()
1553-
.unwrap();
1554-
1555-
// Compute justification and finalization state from progressive balances cache.
1556-
let (_, total_active_balance) = state
1557-
.total_active_balance()
1558-
.ok_or(BeaconStateError::TotalActiveBalanceCacheUninitialized)
1559-
.unwrap();
1560-
1561-
let progressive_total_balances: &ProgressiveTotalBalances =
1562-
state.progressive_total_balances();
1563-
1564-
let (previous_target_balance, current_target_balance) = (
1565-
progressive_total_balances
1566-
.previous_epoch_target_attesting_balance()
1567-
.unwrap(),
1568-
progressive_total_balances
1569-
.current_epoch_target_attesting_balance()
1570-
.unwrap(),
1567+
.map_err(|e| BeaconStateError::ParticipationCacheError(format!("{:?}", e)))?;
1568+
let cached_current_target_balance =
1569+
progressive_total_balances.current_epoch_target_attesting_balance()?;
1570+
debug_assert!(
1571+
handle_zero_effective_balance(current_target_balance) == cached_current_target_balance,
1572+
"Current epoch target attesting balance from progressive cache does not match computed value."
15711573
);
15721574

1573-
let justification_and_finalization_state = weigh_justification_and_finalization(
1574-
JustificationAndFinalizationState::new(state),
1575-
total_active_balance,
1576-
previous_target_balance,
1577-
current_target_balance,
1578-
)
1579-
.unwrap();
1575+
Ok(())
1576+
};
15801577

1581-
assert_eq!(previous_target_balance, expected_previous_target_balance);
1582-
assert_eq!(current_target_balance, expected_current_target_balance);
1583-
assert_eq!(
1584-
justification_and_finalization_state.current_justified_checkpoint(),
1585-
expected_justified_checkpoint
1586-
);
1587-
assert_eq!(
1588-
justification_and_finalization_state.finalized_checkpoint(),
1589-
expected_finalized_checkpoint
1590-
);
1591-
}
1578+
let result: Result<(), BeaconStateError> = do_check();
1579+
debug_assert!(
1580+
result.is_ok(),
1581+
"An error occurred while checking progressive balances: {:?}",
1582+
result.err()
1583+
);
15921584
}
15931585

15941586
/// Helper struct that is used to encode/decode the state of the `ForkChoice` as SSZ bytes.
Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,61 @@
1-
use crate::per_epoch_processing::altair::participation_cache::Error as ParticipationCacheError;
21
use crate::per_epoch_processing::altair::ParticipationCache;
3-
use types::{BeaconState, BeaconStateError, EthSpec};
2+
use std::borrow::Cow;
3+
use types::{BeaconState, BeaconStateError, ChainSpec, EthSpec};
44

5+
/// Initializes the `ProgressiveTotalBalances` cache using balance values from the
6+
/// `ParticipationCache`. If the optional `&ParticipationCache` is not supplied, it will be computed
7+
/// from the `BeaconState`.
58
pub fn initialize_progressive_total_balances<E: EthSpec>(
69
state: &mut BeaconState<E>,
7-
participation_cache: &ParticipationCache,
10+
maybe_participation_cache: Option<&ParticipationCache>,
11+
spec: &ChainSpec,
812
) -> Result<(), BeaconStateError> {
9-
let current_epoch = state.current_epoch();
10-
let progressive_total_balances = state.progressive_total_balances_mut();
11-
12-
let to_beacon_state_error =
13-
|e: ParticipationCacheError| BeaconStateError::ParticipationCacheError(format!("{:?}", e));
14-
15-
let (previous_epoch_target_attesting_balance, current_epoch_target_attesting_balance) = (
16-
participation_cache
17-
.previous_epoch_target_attesting_balance()
18-
.map_err(to_beacon_state_error)?,
19-
participation_cache
20-
.current_epoch_target_attesting_balance()
21-
.map_err(to_beacon_state_error)?,
22-
);
13+
if !is_progressive_balances_enabled(state)
14+
|| state.progressive_total_balances().is_initialized()
15+
{
16+
return Ok(());
17+
}
18+
19+
let participation_cache = match maybe_participation_cache {
20+
Some(cache) => Cow::Borrowed(cache),
21+
None => Cow::Owned(ParticipationCache::new(state, spec)?),
22+
};
23+
24+
// FIXME[JC]: Converts value to 0 if it is the same as `EFFECTIVE_BALANCE_INCREMENT`.
25+
// `ParticipationCache` methods return `EFFECTIVE_BALANCE_INCREMENT` (1,000,000,000)
26+
// when the balance is 0, and this breaks our calculation.
27+
let handle_zero_effective_balance = |val| {
28+
if val == spec.effective_balance_increment {
29+
0
30+
} else {
31+
val
32+
}
33+
};
34+
35+
let previous_epoch_target_attesting_balance = participation_cache
36+
.previous_epoch_target_attesting_balance()
37+
.map(handle_zero_effective_balance)
38+
.map_err(|e| BeaconStateError::ParticipationCacheError(format!("{:?}", e)))?;
2339

24-
progressive_total_balances.initialize(
40+
let current_epoch_target_attesting_balance = participation_cache
41+
.current_epoch_target_attesting_balance()
42+
.map(handle_zero_effective_balance)
43+
.map_err(|e| BeaconStateError::ParticipationCacheError(format!("{:?}", e)))?;
44+
45+
let current_epoch = state.current_epoch();
46+
state.progressive_total_balances_mut().initialize(
2547
current_epoch,
2648
previous_epoch_target_attesting_balance,
2749
current_epoch_target_attesting_balance,
2850
);
2951

3052
Ok(())
3153
}
54+
55+
/// `ProgressiveTotalBalances` is only enabled from `Altair` as it requires `ParticipationCache`.
56+
fn is_progressive_balances_enabled<E: EthSpec>(state: &BeaconState<E>) -> bool {
57+
match state {
58+
BeaconState::Base(_) => false,
59+
BeaconState::Altair(_) | BeaconState::Merge(_) | BeaconState::Capella(_) => true,
60+
}
61+
}

consensus/state_processing/src/per_block_processing.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ use crate::common::decrease_balance;
4242
use crate::StateProcessingStrategy;
4343

4444
use crate::common::initialize_progressive_total_balances::initialize_progressive_total_balances;
45-
use crate::per_epoch_processing::altair::ParticipationCache;
4645
#[cfg(feature = "arbitrary-fuzz")]
4746
use arbitrary::Arbitrary;
4847

@@ -112,14 +111,11 @@ pub fn per_block_processing<T: EthSpec, Payload: AbstractExecPayload<T>>(
112111
.map_err(BlockProcessingError::InconsistentBlockFork)?;
113112

114113
// Verify that the `BeaconState` instantiation matches the fork at `state.slot()`.
115-
let fork_name = state
114+
state
116115
.fork_name(spec)
117116
.map_err(BlockProcessingError::InconsistentStateFork)?;
118117

119-
if fork_name != ForkName::Base && !state.progressive_total_balances().is_initialized() {
120-
let participation_cache = ParticipationCache::new(state, spec)?;
121-
initialize_progressive_total_balances::<T>(state, &participation_cache)?;
122-
}
118+
initialize_progressive_total_balances(state, None, spec)?;
123119

124120
let verify_signatures = match block_signature_strategy {
125121
BlockSignatureStrategy::VerifyBulk => {

consensus/state_processing/src/per_block_processing/process_operations.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,10 @@ pub mod altair {
170170
let validator_effective_balance = state.get_effective_balance(index)?;
171171
state
172172
.progressive_total_balances_mut()
173-
.on_attestation(data.target.epoch, validator_effective_balance)?;
173+
.on_new_target_attestation(
174+
data.target.epoch,
175+
validator_effective_balance,
176+
)?;
174177
}
175178
}
176179
}

consensus/state_processing/src/per_epoch_processing/altair.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub fn process_epoch<T: EthSpec>(
3232
// Pre-compute participating indices and total balances.
3333
let participation_cache = ParticipationCache::new(state, spec)?;
3434
let sync_committee = state.current_sync_committee()?.clone();
35-
initialize_progressive_total_balances::<T>(state, &participation_cache)?;
35+
initialize_progressive_total_balances::<T>(state, Some(&participation_cache), spec)?;
3636

3737
// Justification and finalization.
3838
let justification_and_finalization_state =

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl Balance {
5353
}
5454

5555
/// Caches the participation values for one epoch (either the previous or current).
56-
#[derive(PartialEq, Debug)]
56+
#[derive(PartialEq, Debug, Clone)]
5757
struct SingleEpochParticipationCache {
5858
/// Maps an active validator index to their participation flags.
5959
///
@@ -173,7 +173,7 @@ impl SingleEpochParticipationCache {
173173
}
174174

175175
/// Maintains a cache to be used during `altair::process_epoch`.
176-
#[derive(PartialEq, Debug)]
176+
#[derive(PartialEq, Debug, Clone)]
177177
pub struct ParticipationCache {
178178
current_epoch: Epoch,
179179
/// Caches information about active validators pertaining to `self.current_epoch`.

consensus/state_processing/src/per_epoch_processing/capella.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ pub fn process_epoch<T: EthSpec>(
2828
// Pre-compute participating indices and total balances.
2929
let participation_cache = ParticipationCache::new(state, spec)?;
3030
let sync_committee = state.current_sync_committee()?.clone();
31-
32-
initialize_progressive_total_balances::<T>(state, &participation_cache)?;
31+
initialize_progressive_total_balances(state, Some(&participation_cache), spec)?;
3332

3433
// Justification and finalization.
3534
let justification_and_finalization_state =

consensus/state_processing/src/per_epoch_processing/effective_balance_updates.rs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use types::{BeaconStateError, EthSpec, ProgressiveTotalBalances};
77

88
pub fn process_effective_balance_updates<T: EthSpec>(
99
state: &mut BeaconState<T>,
10-
participation_cache: Option<&ParticipationCache>,
10+
maybe_participation_cache: Option<&ParticipationCache>,
1111
spec: &ChainSpec,
1212
) -> Result<(), EpochProcessingError> {
1313
let hysteresis_increment = spec
@@ -32,13 +32,15 @@ pub fn process_effective_balance_updates<T: EthSpec>(
3232
spec.max_effective_balance,
3333
);
3434

35-
update_progressive_balances(
36-
participation_cache,
37-
progressive_balances_cache,
38-
index,
39-
old_effective_balance,
40-
new_effective_balance,
41-
)?;
35+
if let Some(participation_cache) = maybe_participation_cache {
36+
update_progressive_balances(
37+
participation_cache,
38+
progressive_balances_cache,
39+
index,
40+
old_effective_balance,
41+
new_effective_balance,
42+
)?;
43+
}
4244

4345
validator.effective_balance = new_effective_balance;
4446
}
@@ -47,22 +49,20 @@ pub fn process_effective_balance_updates<T: EthSpec>(
4749
}
4850

4951
fn update_progressive_balances(
50-
participation_cache: Option<&ParticipationCache>,
52+
participation_cache: &ParticipationCache,
5153
progressive_balances_cache: &mut ProgressiveTotalBalances,
5254
index: usize,
5355
old_effective_balance: u64,
5456
new_effective_balance: u64,
5557
) -> Result<(), EpochProcessingError> {
56-
if let Some(participation_cache) = participation_cache {
57-
if old_effective_balance != new_effective_balance {
58-
let is_current_epoch_target_attester =
59-
participation_cache.is_current_epoch_timely_target_attester(index)?;
60-
progressive_balances_cache.on_effective_balance_change(
61-
is_current_epoch_target_attester,
62-
old_effective_balance,
63-
new_effective_balance,
64-
)?;
65-
}
58+
if old_effective_balance != new_effective_balance {
59+
let is_current_epoch_target_attester =
60+
participation_cache.is_current_epoch_timely_target_attester(index)?;
61+
progressive_balances_cache.on_effective_balance_change(
62+
is_current_epoch_target_attester,
63+
old_effective_balance,
64+
new_effective_balance,
65+
)?;
6666
}
6767
Ok(())
6868
}

consensus/state_processing/src/upgrade/altair.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use crate::common::initialize_progressive_total_balances::initialize_progressive_total_balances;
22
use crate::common::{get_attestation_participation_flag_indices, get_attesting_indices};
3-
use crate::per_epoch_processing::altair::ParticipationCache;
43
use std::mem;
54
use std::sync::Arc;
65
use types::{
@@ -113,10 +112,7 @@ pub fn upgrade_to_altair<E: EthSpec>(
113112
// Fill in previous epoch participation from the pre state's pending attestations.
114113
translate_participation(&mut post, &pre.previous_epoch_attestations, spec)?;
115114

116-
if !post.progressive_total_balances().is_initialized() {
117-
let participation_cache = ParticipationCache::new(&post, spec)?;
118-
initialize_progressive_total_balances(&mut post, &participation_cache)?;
119-
}
115+
initialize_progressive_total_balances(&mut post, None, spec)?;
120116

121117
// Fill in sync committees
122118
// Note: A duplicate committee is assigned for the current and next committee at the fork

0 commit comments

Comments
 (0)