Skip to content

Commit 5352d5f

Browse files
authored
Update proposer_slashings and attester_slashings amounts for electra. (#7316)
Did not find a specific issue beside #6821 Leverage `whistleblower_reward_quotient_for_state` to have accurate post-electra `proposer_slashings` and `attester_slashings` fields returned by `/eth/v1/beacon/rewards/blocks/<id>`.
1 parent 6fad6fb commit 5352d5f

File tree

4 files changed

+150
-7
lines changed

4 files changed

+150
-7
lines changed

beacon_node/beacon_chain/src/beacon_block_reward.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
139139
state
140140
.get_validator(proposer_slashing.proposer_index() as usize)?
141141
.effective_balance
142-
.safe_div(self.spec.whistleblower_reward_quotient)?,
142+
.safe_div(self.spec.whistleblower_reward_quotient_for_state(state))?,
143143
)?;
144144
}
145145

@@ -161,7 +161,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
161161
state
162162
.get_validator(attester_index as usize)?
163163
.effective_balance
164-
.safe_div(self.spec.whistleblower_reward_quotient)?,
164+
.safe_div(self.spec.whistleblower_reward_quotient_for_state(state))?,
165165
)?;
166166
}
167167
}

beacon_node/beacon_chain/tests/rewards.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,35 @@ async fn test_rewards_base_inactivity_leak_justification_epoch() {
256256
);
257257
}
258258

259+
#[tokio::test]
260+
async fn test_rewards_electra_slashings() {
261+
let spec = ForkName::Electra.make_genesis_spec(E::default_spec());
262+
let harness = get_electra_harness(spec);
263+
let state = harness.get_current_state();
264+
265+
harness.extend_slots(E::slots_per_epoch() as usize).await;
266+
267+
let mut initial_balances = harness.get_current_state().balances().to_vec();
268+
269+
// add an attester slashing and calculate slashing penalties
270+
harness.add_attester_slashing(vec![0]).unwrap();
271+
let slashed_balance_1 = initial_balances.get_mut(0).unwrap();
272+
let validator_1_effective_balance = state.get_effective_balance(0).unwrap();
273+
let delta_1 = validator_1_effective_balance
274+
/ harness.spec.min_slashing_penalty_quotient_for_state(&state);
275+
*slashed_balance_1 -= delta_1;
276+
277+
// add a proposer slashing and calculating slashing penalties
278+
harness.add_proposer_slashing(1).unwrap();
279+
let slashed_balance_2 = initial_balances.get_mut(1).unwrap();
280+
let validator_2_effective_balance = state.get_effective_balance(1).unwrap();
281+
let delta_2 = validator_2_effective_balance
282+
/ harness.spec.min_slashing_penalty_quotient_for_state(&state);
283+
*slashed_balance_2 -= delta_2;
284+
285+
check_all_electra_rewards(&harness, initial_balances).await;
286+
}
287+
259288
#[tokio::test]
260289
async fn test_rewards_base_slashings() {
261290
let spec = ForkName::Base.make_genesis_spec(E::default_spec());
@@ -696,6 +725,75 @@ async fn test_rewards_base_subset_only() {
696725
check_all_base_rewards_for_subset(&harness, initial_balances, validators_subset).await;
697726
}
698727

728+
async fn check_all_electra_rewards(
729+
harness: &BeaconChainHarness<EphemeralHarnessType<E>>,
730+
mut balances: Vec<u64>,
731+
) {
732+
let mut proposal_rewards_map = HashMap::new();
733+
let mut sync_committee_rewards_map = HashMap::new();
734+
for _ in 0..E::slots_per_epoch() {
735+
let state = harness.get_current_state();
736+
let slot = state.slot() + Slot::new(1);
737+
738+
// calculate beacon block rewards / penalties
739+
let ((signed_block, _maybe_blob_sidecars), mut state) =
740+
harness.make_block_return_pre_state(state, slot).await;
741+
let beacon_block_reward = harness
742+
.chain
743+
.compute_beacon_block_reward(signed_block.message(), &mut state)
744+
.unwrap();
745+
746+
let total_proposer_reward = proposal_rewards_map
747+
.entry(beacon_block_reward.proposer_index)
748+
.or_insert(0);
749+
*total_proposer_reward += beacon_block_reward.total as i64;
750+
751+
// calculate sync committee rewards / penalties
752+
let reward_payload = harness
753+
.chain
754+
.compute_sync_committee_rewards(signed_block.message(), &mut state)
755+
.unwrap();
756+
757+
for reward in reward_payload {
758+
let total_sync_reward = sync_committee_rewards_map
759+
.entry(reward.validator_index)
760+
.or_insert(0);
761+
*total_sync_reward += reward.reward;
762+
}
763+
764+
harness.extend_slots(1).await;
765+
}
766+
767+
// compute reward deltas for all validators in epoch 0
768+
let StandardAttestationRewards {
769+
ideal_rewards,
770+
total_rewards,
771+
} = harness
772+
.chain
773+
.compute_attestation_rewards(Epoch::new(0), vec![])
774+
.unwrap();
775+
776+
// assert ideal rewards are greater than 0
777+
assert_eq!(
778+
ideal_rewards.len() as u64,
779+
harness.spec.max_effective_balance_electra / harness.spec.effective_balance_increment
780+
);
781+
782+
assert!(ideal_rewards
783+
.iter()
784+
.all(|reward| reward.head > 0 && reward.target > 0 && reward.source > 0));
785+
786+
// apply attestation, proposal, and sync committee rewards and penalties to initial balances
787+
apply_attestation_rewards(&mut balances, total_rewards);
788+
apply_other_rewards(&mut balances, &proposal_rewards_map);
789+
apply_other_rewards(&mut balances, &sync_committee_rewards_map);
790+
791+
// verify expected balances against actual balances
792+
let actual_balances: Vec<u64> = harness.get_current_state().balances().to_vec();
793+
794+
assert_eq!(balances, actual_balances);
795+
}
796+
699797
async fn check_all_base_rewards(
700798
harness: &BeaconChainHarness<EphemeralHarnessType<E>>,
701799
balances: Vec<u64>,

beacon_node/http_api/tests/tests.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use beacon_chain::{
44
BeaconChain, ChainConfig, StateSkipConfig, WhenSlotSkipped,
55
};
66
use either::Either;
7+
use eth2::lighthouse::StandardBlockReward;
78
use eth2::{
89
mixin::{RequestAccept, ResponseForkName, ResponseOptional},
910
reqwest::RequestBuilder,
@@ -6381,6 +6382,34 @@ impl ApiTester {
63816382

63826383
assert_eq!(result.execution_optimistic, Some(true));
63836384
}
6385+
6386+
async fn test_get_beacon_rewards_blocks_at_head(&self) -> StandardBlockReward {
6387+
self.client
6388+
.get_beacon_rewards_blocks(CoreBlockId::Head)
6389+
.await
6390+
.unwrap()
6391+
.data
6392+
}
6393+
6394+
async fn test_beacon_block_rewards_electra(self) -> Self {
6395+
for _ in 0..E::slots_per_epoch() {
6396+
let state = self.harness.get_current_state();
6397+
let slot = state.slot() + Slot::new(1);
6398+
// calculate beacon block rewards / penalties
6399+
let ((signed_block, _maybe_blob_sidecars), mut state) =
6400+
self.harness.make_block_return_pre_state(state, slot).await;
6401+
6402+
let beacon_block_reward = self
6403+
.harness
6404+
.chain
6405+
.compute_beacon_block_reward(signed_block.message(), &mut state)
6406+
.unwrap();
6407+
self.harness.extend_slots(1).await;
6408+
let api_beacon_block_reward = self.test_get_beacon_rewards_blocks_at_head().await;
6409+
assert_eq!(beacon_block_reward, api_beacon_block_reward);
6410+
}
6411+
self
6412+
}
63846413
}
63856414

63866415
async fn poll_events<S: Stream<Item = Result<EventKind<E>, eth2::Error>> + Unpin, E: EthSpec>(
@@ -7522,3 +7551,17 @@ async fn expected_withdrawals_valid_capella() {
75227551
.test_get_expected_withdrawals_capella()
75237552
.await;
75247553
}
7554+
7555+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
7556+
async fn get_beacon_rewards_blocks_electra() {
7557+
let mut config = ApiTesterConfig::default();
7558+
config.spec.altair_fork_epoch = Some(Epoch::new(0));
7559+
config.spec.bellatrix_fork_epoch = Some(Epoch::new(0));
7560+
config.spec.capella_fork_epoch = Some(Epoch::new(0));
7561+
config.spec.deneb_fork_epoch = Some(Epoch::new(0));
7562+
config.spec.electra_fork_epoch = Some(Epoch::new(0));
7563+
ApiTester::new_from_config(config)
7564+
.await
7565+
.test_beacon_block_rewards_electra()
7566+
.await;
7567+
}

common/eth2/src/lib.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use derivative::Derivative;
2020
use either::Either;
2121
use futures::Stream;
2222
use futures_util::StreamExt;
23+
use lighthouse::StandardBlockReward;
2324
use lighthouse_network::PeerId;
2425
use pretty_reqwest_error::PrettyReqwestError;
2526
pub use reqwest;
@@ -1677,17 +1678,18 @@ impl BeaconNodeHttpClient {
16771678
}
16781679

16791680
/// `GET beacon/rewards/blocks`
1680-
pub async fn get_beacon_rewards_blocks(&self, epoch: Epoch) -> Result<(), Error> {
1681+
pub async fn get_beacon_rewards_blocks(
1682+
&self,
1683+
block_id: BlockId,
1684+
) -> Result<GenericResponse<StandardBlockReward>, Error> {
16811685
let mut path = self.eth_path(V1)?;
16821686

16831687
path.path_segments_mut()
16841688
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
16851689
.push("beacon")
16861690
.push("rewards")
1687-
.push("blocks");
1688-
1689-
path.query_pairs_mut()
1690-
.append_pair("epoch", &epoch.to_string());
1691+
.push("blocks")
1692+
.push(&block_id.to_string());
16911693

16921694
self.get(path).await
16931695
}

0 commit comments

Comments
 (0)