Fix for issue 4860 - Added in process_justification_and_finalization (#4877)
* Added in process_justification_and_finalization Added in process_justification_and_finalization to compute_attestation_rewards_altair to take into account justified attestations when coming out of inactivity leak. Also added in test to check for this edge case. * Added in justification and finalization for compute_attestation_rewards_base * Added in test for altair rewards without inactivity leak
This commit is contained in:
parent
051c3e842f
commit
e181741d38
@ -5,7 +5,9 @@ use participation_cache::ParticipationCache;
|
|||||||
use safe_arith::SafeArith;
|
use safe_arith::SafeArith;
|
||||||
use serde_utils::quoted_u64::Quoted;
|
use serde_utils::quoted_u64::Quoted;
|
||||||
use slog::debug;
|
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::{
|
use state_processing::{
|
||||||
common::altair::BaseRewardPerIncrement,
|
common::altair::BaseRewardPerIncrement,
|
||||||
per_epoch_processing::altair::{participation_cache, rewards_and_penalties::get_flag_weight},
|
per_epoch_processing::altair::{participation_cache, rewards_and_penalties::get_flag_weight},
|
||||||
@ -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::validator_statuses::InclusionInfo;
|
||||||
use state_processing::per_epoch_processing::base::{
|
use state_processing::per_epoch_processing::base::{
|
||||||
|
process_justification_and_finalization as process_justification_and_finalization_base,
|
||||||
TotalBalances, ValidatorStatus, ValidatorStatuses,
|
TotalBalances, ValidatorStatus, ValidatorStatuses,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -67,6 +70,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
let mut validator_statuses = ValidatorStatuses::new(&state, spec)?;
|
let mut validator_statuses = ValidatorStatuses::new(&state, spec)?;
|
||||||
validator_statuses.process_attestations(&state)?;
|
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 =
|
let ideal_rewards =
|
||||||
self.compute_ideal_rewards_base(&state, &validator_statuses.total_balances)?;
|
self.compute_ideal_rewards_base(&state, &validator_statuses.total_balances)?;
|
||||||
|
|
||||||
@ -125,6 +135,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
|
|
||||||
// Calculate ideal_rewards
|
// Calculate ideal_rewards
|
||||||
let participation_cache = ParticipationCache::new(&state, spec)?;
|
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)?;
|
process_inactivity_updates(&mut state, &participation_cache, spec)?;
|
||||||
|
|
||||||
let previous_epoch = state.previous_epoch();
|
let previous_epoch = state.previous_epoch();
|
||||||
|
@ -219,6 +219,156 @@ async fn test_verify_attestation_rewards_base_inactivity_leak() {
|
|||||||
assert_eq!(expected_balances, balances);
|
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]
|
#[tokio::test]
|
||||||
async fn test_verify_attestation_rewards_altair_inactivity_leak() {
|
async fn test_verify_attestation_rewards_altair_inactivity_leak() {
|
||||||
let spec = ForkName::Altair.make_genesis_spec(E::default_spec());
|
let spec = ForkName::Altair.make_genesis_spec(E::default_spec());
|
||||||
@ -313,6 +463,115 @@ async fn test_verify_attestation_rewards_altair_inactivity_leak() {
|
|||||||
assert_eq!(expected_balances, balances);
|
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]
|
#[tokio::test]
|
||||||
async fn test_verify_attestation_rewards_base_subset_only() {
|
async fn test_verify_attestation_rewards_base_subset_only() {
|
||||||
let harness = get_harness(E::default_spec());
|
let harness = get_harness(E::default_spec());
|
||||||
|
Loading…
Reference in New Issue
Block a user