Skip to content

Commit 94a1bac

Browse files
#4512 inactivity calculation for Altair (#4807)
## Issue Addressed #4512 Which issue # does this PR address? ## Proposed Changes Add inactivity calculation for Altair Please list or describe the changes introduced by this PR. Add inactivity calculation for Altair ## Additional Info Please provide any additional information. For example, future considerations or information useful for reviewers. Co-authored-by: Jimmy Chen <[email protected]>
1 parent 90f78d1 commit 94a1bac

File tree

3 files changed

+159
-4
lines changed

3 files changed

+159
-4
lines changed

beacon_node/beacon_chain/src/attestation_rewards.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use participation_cache::ParticipationCache;
55
use safe_arith::SafeArith;
66
use serde_utils::quoted_u64::Quoted;
77
use slog::debug;
8+
use state_processing::per_epoch_processing::altair::process_inactivity_updates;
89
use state_processing::{
910
common::altair::BaseRewardPerIncrement,
1011
per_epoch_processing::altair::{participation_cache, rewards_and_penalties::get_flag_weight},
@@ -124,6 +125,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
124125

125126
// Calculate ideal_rewards
126127
let participation_cache = ParticipationCache::new(&state, spec)?;
128+
process_inactivity_updates(&mut state, &participation_cache, spec)?;
127129

128130
let previous_epoch = state.previous_epoch();
129131

@@ -190,6 +192,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
190192
let mut head_reward = 0i64;
191193
let mut target_reward = 0i64;
192194
let mut source_reward = 0i64;
195+
let mut inactivity_penalty = 0i64;
193196

194197
if eligible {
195198
let effective_balance = state.get_effective_balance(*validator_index)?;
@@ -215,6 +218,14 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
215218
head_reward = 0;
216219
} else if flag_index == TIMELY_TARGET_FLAG_INDEX {
217220
target_reward = *penalty;
221+
222+
let penalty_numerator = effective_balance
223+
.safe_mul(state.get_inactivity_score(*validator_index)?)?;
224+
let penalty_denominator = spec
225+
.inactivity_score_bias
226+
.safe_mul(spec.inactivity_penalty_quotient_for_state(&state))?;
227+
inactivity_penalty =
228+
-(penalty_numerator.safe_div(penalty_denominator)? as i64);
218229
} else if flag_index == TIMELY_SOURCE_FLAG_INDEX {
219230
source_reward = *penalty;
220231
}
@@ -226,8 +237,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
226237
target: target_reward,
227238
source: source_reward,
228239
inclusion_delay: None,
229-
// TODO: altair calculation logic needs to be updated to include inactivity penalty
230-
inactivity: 0,
240+
inactivity: inactivity_penalty,
231241
});
232242
}
233243

@@ -250,7 +260,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
250260
target: 0,
251261
source: 0,
252262
inclusion_delay: None,
253-
// TODO: altair calculation logic needs to be updated to include inactivity penalty
254263
inactivity: 0,
255264
});
256265
match *flag_index {

beacon_node/beacon_chain/src/test_utils.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2350,6 +2350,29 @@ where
23502350
.await
23512351
}
23522352

2353+
/// Uses `Self::extend_chain` to `num_slots` blocks.
2354+
///
2355+
/// Utilizes:
2356+
///
2357+
/// - BlockStrategy::OnCanonicalHead,
2358+
/// - AttestationStrategy::SomeValidators(validators),
2359+
pub async fn extend_slots_some_validators(
2360+
&self,
2361+
num_slots: usize,
2362+
validators: Vec<usize>,
2363+
) -> Hash256 {
2364+
if self.chain.slot().unwrap() == self.chain.canonical_head.cached_head().head_slot() {
2365+
self.advance_slot();
2366+
}
2367+
2368+
self.extend_chain(
2369+
num_slots,
2370+
BlockStrategy::OnCanonicalHead,
2371+
AttestationStrategy::SomeValidators(validators),
2372+
)
2373+
.await
2374+
}
2375+
23532376
/// Extend the `BeaconChain` with some blocks and attestations. Returns the root of the
23542377
/// last-produced block (the head of the chain).
23552378
///

beacon_node/beacon_chain/tests/rewards.rs

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use eth2::lighthouse::StandardAttestationRewards;
1414
use eth2::types::ValidatorId;
1515
use lazy_static::lazy_static;
1616
use types::beacon_state::Error as BeaconStateError;
17-
use types::{BeaconState, ChainSpec};
17+
use types::{BeaconState, ChainSpec, ForkName, Slot};
1818

1919
pub const VALIDATOR_COUNT: usize = 64;
2020

@@ -219,6 +219,100 @@ async fn test_verify_attestation_rewards_base_inactivity_leak() {
219219
assert_eq!(expected_balances, balances);
220220
}
221221

222+
#[tokio::test]
223+
async fn test_verify_attestation_rewards_altair_inactivity_leak() {
224+
let spec = ForkName::Altair.make_genesis_spec(E::default_spec());
225+
let harness = get_harness(spec.clone());
226+
227+
let half = VALIDATOR_COUNT / 2;
228+
let half_validators: Vec<usize> = (0..half).collect();
229+
// target epoch is the epoch where the chain enters inactivity leak
230+
let target_epoch = &spec.min_epochs_to_inactivity_penalty + 1;
231+
232+
// advance until beginning of epoch N + 1 and get balances
233+
harness
234+
.extend_slots_some_validators(
235+
(E::slots_per_epoch() * (target_epoch + 1)) as usize,
236+
half_validators.clone(),
237+
)
238+
.await;
239+
let initial_balances: Vec<u64> = harness.get_current_state().balances().clone().into();
240+
241+
// advance until epoch N + 2 and build proposal rewards map
242+
let mut proposal_rewards_map: HashMap<u64, u64> = HashMap::new();
243+
let mut sync_committee_rewards_map: HashMap<u64, i64> = HashMap::new();
244+
for _ in 0..E::slots_per_epoch() {
245+
let state = harness.get_current_state();
246+
let slot = state.slot() + Slot::new(1);
247+
248+
// calculate beacon block rewards / penalties
249+
let ((signed_block, _maybe_blob_sidecars), mut state) =
250+
harness.make_block_return_pre_state(state, slot).await;
251+
let beacon_block_reward = harness
252+
.chain
253+
.compute_beacon_block_reward(
254+
signed_block.message(),
255+
signed_block.canonical_root(),
256+
&mut state,
257+
)
258+
.unwrap();
259+
260+
let total_proposer_reward = proposal_rewards_map
261+
.get(&beacon_block_reward.proposer_index)
262+
.unwrap_or(&0u64)
263+
+ beacon_block_reward.total;
264+
265+
proposal_rewards_map.insert(beacon_block_reward.proposer_index, total_proposer_reward);
266+
267+
// calculate sync committee rewards / penalties
268+
let reward_payload = harness
269+
.chain
270+
.compute_sync_committee_rewards(signed_block.message(), &mut state)
271+
.unwrap();
272+
273+
reward_payload.iter().for_each(|reward| {
274+
let mut amount = *sync_committee_rewards_map
275+
.get(&reward.validator_index)
276+
.unwrap_or(&0);
277+
amount += reward.reward;
278+
sync_committee_rewards_map.insert(reward.validator_index, amount);
279+
});
280+
281+
harness
282+
.extend_slots_some_validators(1, half_validators.clone())
283+
.await;
284+
}
285+
286+
// compute reward deltas for all validators in epoch N
287+
let StandardAttestationRewards {
288+
ideal_rewards,
289+
total_rewards,
290+
} = harness
291+
.chain
292+
.compute_attestation_rewards(Epoch::new(target_epoch), vec![])
293+
.unwrap();
294+
295+
// assert inactivity penalty for both ideal rewards and individual validators
296+
assert!(ideal_rewards.iter().all(|reward| reward.inactivity == 0));
297+
assert!(total_rewards[..half]
298+
.iter()
299+
.all(|reward| reward.inactivity == 0));
300+
assert!(total_rewards[half..]
301+
.iter()
302+
.all(|reward| reward.inactivity < 0));
303+
304+
// apply attestation, proposal, and sync committee rewards and penalties to initial balances
305+
let expected_balances = apply_attestation_rewards(&initial_balances, total_rewards);
306+
let expected_balances = apply_beacon_block_rewards(&proposal_rewards_map, expected_balances);
307+
let expected_balances =
308+
apply_sync_committee_rewards(&sync_committee_rewards_map, expected_balances);
309+
310+
// verify expected balances against actual balances
311+
let balances: Vec<u64> = harness.get_current_state().balances().clone().into();
312+
313+
assert_eq!(expected_balances, balances);
314+
}
315+
222316
#[tokio::test]
223317
async fn test_verify_attestation_rewards_base_subset_only() {
224318
let harness = get_harness(E::default_spec());
@@ -297,3 +391,32 @@ fn get_validator_balances(state: BeaconState<E>, validators: &[usize]) -> Vec<u6
297391
})
298392
.collect()
299393
}
394+
395+
fn apply_beacon_block_rewards(
396+
proposal_rewards_map: &HashMap<u64, u64>,
397+
expected_balances: Vec<u64>,
398+
) -> Vec<u64> {
399+
let calculated_balances = expected_balances
400+
.iter()
401+
.enumerate()
402+
.map(|(i, balance)| balance + proposal_rewards_map.get(&(i as u64)).unwrap_or(&0u64))
403+
.collect();
404+
405+
calculated_balances
406+
}
407+
408+
fn apply_sync_committee_rewards(
409+
sync_committee_rewards_map: &HashMap<u64, i64>,
410+
expected_balances: Vec<u64>,
411+
) -> Vec<u64> {
412+
let calculated_balances = expected_balances
413+
.iter()
414+
.enumerate()
415+
.map(|(i, balance)| {
416+
(*balance as i64 + sync_committee_rewards_map.get(&(i as u64)).unwrap_or(&0i64))
417+
.unsigned_abs()
418+
})
419+
.collect();
420+
421+
calculated_balances
422+
}

0 commit comments

Comments
 (0)