Skip to content
Closed
13 changes: 10 additions & 3 deletions beacon_node/beacon_chain/src/attestation_rewards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
let mut head_reward = 0i64;
let mut target_reward = 0i64;
let mut source_reward = 0i64;
let mut inactivity_penalty = 0i64;

if eligible {
let effective_balance = state.get_effective_balance(*validator_index)?;
Expand All @@ -214,6 +215,14 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
head_reward = 0;
} else if flag_index == TIMELY_TARGET_FLAG_INDEX {
target_reward = *penalty;

let penalty_numerator = effective_balance
.safe_mul(state.get_inactivity_score(*validator_index)?)?;
let penalty_denominator = spec
.inactivity_score_bias
.safe_mul(spec.inactivity_penalty_quotient_for_state(&state))?;
inactivity_penalty =
penalty_numerator.safe_div(penalty_denominator)? as i64;
} else if flag_index == TIMELY_SOURCE_FLAG_INDEX {
source_reward = *penalty;
}
Expand All @@ -225,8 +234,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
target: target_reward,
source: source_reward,
inclusion_delay: None,
// TODO: altair calculation logic needs to be updated to include inactivity penalty
inactivity: 0,
inactivity: inactivity_penalty * -1,
});
}

Expand All @@ -249,7 +257,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
target: 0,
source: 0,
inclusion_delay: None,
// TODO: altair calculation logic needs to be updated to include inactivity penalty
inactivity: 0,
});
match *flag_index {
Expand Down
71 changes: 70 additions & 1 deletion beacon_node/beacon_chain/tests/rewards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use eth2::lighthouse::StandardAttestationRewards;
use eth2::types::ValidatorId;
use lazy_static::lazy_static;
use types::beacon_state::Error as BeaconStateError;
use types::{BeaconState, ChainSpec};
use types::{BeaconState, ChainSpec, ForkName};

pub const VALIDATOR_COUNT: usize = 64;

Expand Down Expand Up @@ -219,6 +219,75 @@ async fn test_verify_attestation_rewards_base_inactivity_leak() {
assert_eq!(expected_balances, balances);
}

#[tokio::test]
async fn test_verify_attestation_rewards_altair_inactivity_leak() {
let spec = ForkName::Altair.make_genesis_spec(E::default_spec());
let harness = get_harness(spec.clone());

let half = VALIDATOR_COUNT / 2;
let half_validators: Vec<usize> = (0..half).collect();
// target epoch is the epoch where the chain enters inactivity leak
let target_epoch = &spec.min_epochs_to_inactivity_penalty + 2;

// advance until beginning of epoch N + 1 and get balances
harness
.extend_chain(
(E::slots_per_epoch() * (target_epoch + 1)) as usize,
BlockStrategy::OnCanonicalHead,
AttestationStrategy::SomeValidators(half_validators.clone()),
)
.await;
let initial_balances: Vec<u64> = harness.get_current_state().balances().clone().into();

// extend slots to beginning of epoch N + 2
harness.advance_slot();
harness
.extend_chain(
E::slots_per_epoch() as usize,
BlockStrategy::OnCanonicalHead,
AttestationStrategy::SomeValidators(half_validators),
)
.await;
let _slot = harness.get_current_slot();

// compute reward deltas for all validators in epoch N
let StandardAttestationRewards {
ideal_rewards,
total_rewards,
} = harness
.chain
.compute_attestation_rewards(Epoch::new(target_epoch), vec![])
.unwrap();

// assert inactivity penalty for both ideal rewards and individual validators
assert!(ideal_rewards.iter().all(|reward| reward.inactivity == 0));
assert!(total_rewards[..half]
.iter()
.all(|reward| reward.inactivity == 0));
assert!(total_rewards[half..]
.iter()
.all(|reward| reward.inactivity < 0));

// apply attestation rewards to initial balances
let expected_balances = apply_attestation_rewards(&initial_balances, total_rewards);

for initial_balance in &initial_balances {
println!("initial {initial_balance}");
}

for expected_balance in &expected_balances {
println!("expected {expected_balance}");
}
// verify expected balances against actual balances
let balances: Vec<u64> = harness.get_current_state().balances().clone().into();

for balance in &balances {
println!("actual {balance}");
}
// Failing test statement
assert_eq!(expected_balances, balances);
}

#[tokio::test]
async fn test_verify_attestation_rewards_base_subset_only() {
let harness = get_harness(E::default_spec());
Expand Down