Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for issue 4860 - Added in process_justification_and_finalization #4877

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion beacon_node/beacon_chain/src/attestation_rewards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ use participation_cache::ParticipationCache;
use safe_arith::SafeArith;
use serde_utils::quoted_u64::Quoted;
use slog::debug;
use state_processing::per_epoch_processing::altair::process_inactivity_updates;
use state_processing::per_epoch_processing::altair::{
process_inactivity_updates, process_justification_and_finalization,
};
use state_processing::{
common::altair::BaseRewardPerIncrement,
per_epoch_processing::altair::{participation_cache, rewards_and_penalties::get_flag_weight},
Expand All @@ -27,6 +29,7 @@ use state_processing::per_epoch_processing::base::rewards_and_penalties::{
};
use state_processing::per_epoch_processing::base::validator_statuses::InclusionInfo;
use state_processing::per_epoch_processing::base::{
process_justification_and_finalization as process_justification_and_finalization_base,
TotalBalances, ValidatorStatus, ValidatorStatuses,
};

Expand Down Expand Up @@ -67,6 +70,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
let mut validator_statuses = ValidatorStatuses::new(&state, spec)?;
validator_statuses.process_attestations(&state)?;

process_justification_and_finalization_base(
&state,
&validator_statuses.total_balances,
spec,
)?
.apply_changes_to_state(&mut state);

let ideal_rewards =
self.compute_ideal_rewards_base(&state, &validator_statuses.total_balances)?;

Expand Down Expand Up @@ -125,6 +135,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {

// Calculate ideal_rewards
let participation_cache = ParticipationCache::new(&state, spec)?;
process_justification_and_finalization(&state, &participation_cache)?
.apply_changes_to_state(&mut state);
process_inactivity_updates(&mut state, &participation_cache, spec)?;

let previous_epoch = state.previous_epoch();
Expand Down
259 changes: 259 additions & 0 deletions beacon_node/beacon_chain/tests/rewards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,156 @@ async fn test_verify_attestation_rewards_base_inactivity_leak() {
assert_eq!(expected_balances, balances);
}

#[tokio::test]
async fn test_verify_attestation_rewards_base_inactivity_leak_justification_epoch() {
let 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 mut target_epoch = &spec.min_epochs_to_inactivity_penalty + 2;

// advance until beginning of epoch N + 2
harness
.extend_chain(
(E::slots_per_epoch() * (target_epoch + 1)) as usize,
BlockStrategy::OnCanonicalHead,
AttestationStrategy::SomeValidators(half_validators.clone()),
)
.await;

// advance to create first justification epoch and get initial balances
harness.extend_slots(E::slots_per_epoch() as usize).await;
target_epoch += 1;
let initial_balances: Vec<u64> = harness.get_current_state().balances().clone().into();

//assert previous_justified_checkpoint matches 0 as we were in inactivity leak from beginning
assert_eq!(
0,
harness
.get_current_state()
.previous_justified_checkpoint()
.epoch
.as_u64()
);

// extend slots to beginning of epoch N + 1
harness.extend_slots(E::slots_per_epoch() as usize).await;

//assert target epoch and previous_justified_checkpoint match
assert_eq!(
target_epoch,
harness
.get_current_state()
.previous_justified_checkpoint()
.epoch
.as_u64()
);

// 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 we successfully get ideal rewards for justified epoch out of inactivity leak
assert!(ideal_rewards
.iter()
.all(|reward| reward.head > 0 && reward.target > 0 && reward.source > 0));

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

// verify expected balances against actual balances
let balances: Vec<u64> = harness.get_current_state().balances().clone().into();
assert_eq!(expected_balances, balances);
}

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

// advance until epoch N + 1 and get initial balances
harness
.extend_slots((E::slots_per_epoch() * (target_epoch + 1)) as usize)
.await;
let initial_balances: Vec<u64> = harness.get_current_state().balances().clone().into();

// advance until epoch N + 2 and build proposal rewards map
let mut proposal_rewards_map: HashMap<u64, u64> = HashMap::new();
let mut sync_committee_rewards_map: HashMap<u64, i64> = HashMap::new();
for _ in 0..E::slots_per_epoch() {
let state = harness.get_current_state();
let slot = state.slot() + Slot::new(1);

// calculate beacon block rewards / penalties
let ((signed_block, _maybe_blob_sidecars), mut state) =
harness.make_block_return_pre_state(state, slot).await;
let beacon_block_reward = harness
.chain
.compute_beacon_block_reward(
signed_block.message(),
signed_block.canonical_root(),
&mut state,
)
.unwrap();

let total_proposer_reward = proposal_rewards_map
.get(&beacon_block_reward.proposer_index)
.unwrap_or(&0u64)
+ beacon_block_reward.total;

proposal_rewards_map.insert(beacon_block_reward.proposer_index, total_proposer_reward);

// calculate sync committee rewards / penalties
let reward_payload = harness
.chain
.compute_sync_committee_rewards(signed_block.message(), &mut state)
.unwrap();

reward_payload.iter().for_each(|reward| {
let mut amount = *sync_committee_rewards_map
.get(&reward.validator_index)
.unwrap_or(&0);
amount += reward.reward;
sync_committee_rewards_map.insert(reward.validator_index, amount);
});

harness.extend_slots(1).await;
}

// 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 ideal rewards are greater than 0
assert!(ideal_rewards
.iter()
.all(|reward| reward.head > 0 && reward.target > 0 && reward.source > 0));

// apply attestation, proposal, and sync committee rewards and penalties to initial balances
let expected_balances = apply_attestation_rewards(&initial_balances, total_rewards);
let expected_balances = apply_beacon_block_rewards(&proposal_rewards_map, expected_balances);
let expected_balances =
apply_sync_committee_rewards(&sync_committee_rewards_map, expected_balances);

// verify expected balances against actual balances
let balances: Vec<u64> = harness.get_current_state().balances().clone().into();

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());
Expand Down Expand Up @@ -313,6 +463,115 @@ async fn test_verify_attestation_rewards_altair_inactivity_leak() {
assert_eq!(expected_balances, balances);
}

#[tokio::test]
async fn test_verify_attestation_rewards_altair_inactivity_leak_justification_epoch() {
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 + 1
let mut target_epoch = &spec.min_epochs_to_inactivity_penalty + 2;

// advance until beginning of epoch N + 1
harness
.extend_slots_some_validators(
(E::slots_per_epoch() * (target_epoch + 1)) as usize,
half_validators.clone(),
)
.await;

let validator_inactivity_score = harness
.get_current_state()
.get_inactivity_score(VALIDATOR_COUNT - 1)
.unwrap();

//assert to ensure we are in inactivity leak
assert_eq!(4, validator_inactivity_score);

// advance for first justification epoch and get balances
harness.extend_slots(E::slots_per_epoch() as usize).await;
target_epoch += 1;
let initial_balances: Vec<u64> = harness.get_current_state().balances().clone().into();

// advance until epoch N + 2 and build proposal rewards map
let mut proposal_rewards_map: HashMap<u64, u64> = HashMap::new();
let mut sync_committee_rewards_map: HashMap<u64, i64> = HashMap::new();
for _ in 0..E::slots_per_epoch() {
let state = harness.get_current_state();
let slot = state.slot() + Slot::new(1);

// calculate beacon block rewards / penalties
let ((signed_block, _maybe_blob_sidecars), mut state) =
harness.make_block_return_pre_state(state, slot).await;
let beacon_block_reward = harness
.chain
.compute_beacon_block_reward(
signed_block.message(),
signed_block.canonical_root(),
&mut state,
)
.unwrap();

let total_proposer_reward = proposal_rewards_map
.get(&beacon_block_reward.proposer_index)
.unwrap_or(&0u64)
+ beacon_block_reward.total;

proposal_rewards_map.insert(beacon_block_reward.proposer_index, total_proposer_reward);

// calculate sync committee rewards / penalties
let reward_payload = harness
.chain
.compute_sync_committee_rewards(signed_block.message(), &mut state)
.unwrap();

reward_payload.iter().for_each(|reward| {
let mut amount = *sync_committee_rewards_map
.get(&reward.validator_index)
.unwrap_or(&0);
amount += reward.reward;
sync_committee_rewards_map.insert(reward.validator_index, amount);
});

harness.extend_slots(1).await;
}

//assert target epoch and previous_justified_checkpoint match
assert_eq!(
target_epoch,
harness
.get_current_state()
.previous_justified_checkpoint()
.epoch
.as_u64()
);

// 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 ideal rewards are greater than 0
assert!(ideal_rewards
.iter()
.all(|reward| reward.head > 0 && reward.target > 0 && reward.source > 0));

// apply attestation, proposal, and sync committee rewards and penalties to initial balances
let expected_balances = apply_attestation_rewards(&initial_balances, total_rewards);
let expected_balances = apply_beacon_block_rewards(&proposal_rewards_map, expected_balances);
let expected_balances =
apply_sync_committee_rewards(&sync_committee_rewards_map, expected_balances);

// verify expected balances against actual balances
let balances: Vec<u64> = harness.get_current_state().balances().clone().into();
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