Update attestation rewards API for Electra (#6819)

Closes:

- https://github.com/sigp/lighthouse/issues/6818


  Use `MAX_EFFECTIVE_BALANCE_ELECTRA` (2048) for attestation reward calculations involving Electra.

Add a new `InteropGenesisBuilder` that tries to provide a more flexible way to build genesis states. Unfortunately due to lifetime jank, it is quite unergonomic at present. We may want to refactor this builder in future to make it easier to use.
This commit is contained in:
Michael Sproul
2025-02-09 21:15:33 +11:00
committed by GitHub
parent 6032f15890
commit 0344f68cfd
6 changed files with 341 additions and 134 deletions

View File

@@ -36,6 +36,38 @@ fn get_harness(spec: ChainSpec) -> BeaconChainHarness<EphemeralHarnessType<E>> {
.keypairs(KEYPAIRS.to_vec())
.fresh_ephemeral_store()
.chain_config(chain_config)
.mock_execution_layer()
.build();
harness.advance_slot();
harness
}
fn get_electra_harness(spec: ChainSpec) -> BeaconChainHarness<EphemeralHarnessType<E>> {
let chain_config = ChainConfig {
reconstruct_historic_states: true,
..Default::default()
};
let spec = Arc::new(spec);
let harness = BeaconChainHarness::builder(E::default())
.spec(spec.clone())
.keypairs(KEYPAIRS.to_vec())
.with_genesis_state_builder(|builder| {
builder.set_initial_balance_fn(Box::new(move |i| {
// Use a variety of balances between min activation balance and max effective balance.
let balance = spec.max_effective_balance_electra
/ (i as u64 + 1)
/ spec.effective_balance_increment
* spec.effective_balance_increment;
balance.max(spec.min_activation_balance)
}))
})
.fresh_ephemeral_store()
.chain_config(chain_config)
.mock_execution_layer()
.build();
harness.advance_slot();
@@ -560,6 +592,83 @@ async fn test_rewards_altair_inactivity_leak_justification_epoch() {
assert_eq!(expected_balances, balances);
}
#[tokio::test]
async fn test_rewards_electra() {
let spec = ForkName::Electra.make_genesis_spec(E::default_spec());
let harness = get_electra_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 mut expected_balances = harness.get_current_state().balances().to_vec();
// advance until epoch N + 2 and build proposal rewards map
let mut proposal_rewards_map = HashMap::new();
let mut sync_committee_rewards_map = 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(), &mut state)
.unwrap();
let total_proposer_reward = proposal_rewards_map
.entry(beacon_block_reward.proposer_index)
.or_insert(0);
*total_proposer_reward += beacon_block_reward.total as i64;
// calculate sync committee rewards / penalties
let reward_payload = harness
.chain
.compute_sync_committee_rewards(signed_block.message(), &mut state)
.unwrap();
for reward in reward_payload {
let total_sync_reward = sync_committee_rewards_map
.entry(reward.validator_index)
.or_insert(0);
*total_sync_reward += reward.reward;
}
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_eq!(
ideal_rewards.len() as u64,
spec.max_effective_balance_electra / spec.effective_balance_increment
);
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
apply_attestation_rewards(&mut expected_balances, total_rewards);
apply_other_rewards(&mut expected_balances, &proposal_rewards_map);
apply_other_rewards(&mut expected_balances, &sync_committee_rewards_map);
// verify expected balances against actual balances
let balances: Vec<u64> = harness.get_current_state().balances().to_vec();
assert_eq!(expected_balances, balances);
}
#[tokio::test]
async fn test_rewards_base_subset_only() {
let spec = ForkName::Base.make_genesis_spec(E::default_spec());