Skip to content

Commit 350ae55

Browse files
authored
Merge of #6819
2 parents 3040f03 + 3539bcc commit 350ae55

File tree

6 files changed

+341
-134
lines changed

6 files changed

+341
-134
lines changed

beacon_node/beacon_chain/src/attestation_rewards.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
175175
let base_reward_per_increment =
176176
BaseRewardPerIncrement::new(total_active_balance, spec)?;
177177

178-
for effective_balance_eth in 1..=self.max_effective_balance_increment_steps()? {
178+
for effective_balance_eth in
179+
1..=self.max_effective_balance_increment_steps(previous_epoch)?
180+
{
179181
let effective_balance =
180182
effective_balance_eth.safe_mul(spec.effective_balance_increment)?;
181183
let base_reward =
@@ -321,11 +323,14 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
321323
})
322324
}
323325

324-
fn max_effective_balance_increment_steps(&self) -> Result<u64, BeaconChainError> {
326+
fn max_effective_balance_increment_steps(
327+
&self,
328+
rewards_epoch: Epoch,
329+
) -> Result<u64, BeaconChainError> {
325330
let spec = &self.spec;
326-
let max_steps = spec
327-
.max_effective_balance
328-
.safe_div(spec.effective_balance_increment)?;
331+
let fork_name = spec.fork_name_at_epoch(rewards_epoch);
332+
let max_effective_balance = spec.max_effective_balance_for_fork(fork_name);
333+
let max_steps = max_effective_balance.safe_div(spec.effective_balance_increment)?;
329334
Ok(max_steps)
330335
}
331336

@@ -386,7 +391,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
386391

387392
let mut ideal_attestation_rewards_list = Vec::new();
388393
let sqrt_total_active_balance = SqrtTotalActiveBalance::new(total_balances.current_epoch());
389-
for effective_balance_step in 1..=self.max_effective_balance_increment_steps()? {
394+
for effective_balance_step in
395+
1..=self.max_effective_balance_increment_steps(previous_epoch)?
396+
{
390397
let effective_balance =
391398
effective_balance_step.safe_mul(spec.effective_balance_increment)?;
392399
let base_reward =

beacon_node/beacon_chain/src/test_utils.rs

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use execution_layer::{
3131
ExecutionLayer,
3232
};
3333
use futures::channel::mpsc::Receiver;
34-
pub use genesis::{interop_genesis_state_with_eth1, DEFAULT_ETH1_BLOCK_HASH};
34+
pub use genesis::{InteropGenesisBuilder, DEFAULT_ETH1_BLOCK_HASH};
3535
use int_to_bytes::int_to_bytes32;
3636
use kzg::trusted_setup::get_trusted_setup;
3737
use kzg::{Kzg, TrustedSetup};
@@ -232,6 +232,7 @@ pub struct Builder<T: BeaconChainTypes> {
232232
mock_execution_layer: Option<MockExecutionLayer<T::EthSpec>>,
233233
testing_slot_clock: Option<TestingSlotClock>,
234234
validator_monitor_config: Option<ValidatorMonitorConfig>,
235+
genesis_state_builder: Option<InteropGenesisBuilder<T::EthSpec>>,
235236
import_all_data_columns: bool,
236237
runtime: TestRuntime,
237238
log: Logger,
@@ -253,16 +254,22 @@ impl<E: EthSpec> Builder<EphemeralHarnessType<E>> {
253254
)
254255
.unwrap(),
255256
);
257+
let genesis_state_builder = self.genesis_state_builder.take().unwrap_or_else(|| {
258+
// Set alternating withdrawal credentials if no builder is specified.
259+
InteropGenesisBuilder::default().set_alternating_eth1_withdrawal_credentials()
260+
});
261+
256262
let mutator = move |builder: BeaconChainBuilder<_>| {
257263
let header = generate_genesis_header::<E>(builder.get_spec(), false);
258-
let genesis_state = interop_genesis_state_with_eth1::<E>(
259-
&validator_keypairs,
260-
HARNESS_GENESIS_TIME,
261-
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
262-
header,
263-
builder.get_spec(),
264-
)
265-
.expect("should generate interop state");
264+
let genesis_state = genesis_state_builder
265+
.set_opt_execution_payload_header(header)
266+
.build_genesis_state(
267+
&validator_keypairs,
268+
HARNESS_GENESIS_TIME,
269+
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
270+
builder.get_spec(),
271+
)
272+
.expect("should generate interop state");
266273
builder
267274
.genesis_state(genesis_state)
268275
.expect("should build state using recent genesis")
@@ -318,16 +325,22 @@ impl<E: EthSpec> Builder<DiskHarnessType<E>> {
318325
.clone()
319326
.expect("cannot build without validator keypairs");
320327

328+
let genesis_state_builder = self.genesis_state_builder.take().unwrap_or_else(|| {
329+
// Set alternating withdrawal credentials if no builder is specified.
330+
InteropGenesisBuilder::default().set_alternating_eth1_withdrawal_credentials()
331+
});
332+
321333
let mutator = move |builder: BeaconChainBuilder<_>| {
322334
let header = generate_genesis_header::<E>(builder.get_spec(), false);
323-
let genesis_state = interop_genesis_state_with_eth1::<E>(
324-
&validator_keypairs,
325-
HARNESS_GENESIS_TIME,
326-
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
327-
header,
328-
builder.get_spec(),
329-
)
330-
.expect("should generate interop state");
335+
let genesis_state = genesis_state_builder
336+
.set_opt_execution_payload_header(header)
337+
.build_genesis_state(
338+
&validator_keypairs,
339+
HARNESS_GENESIS_TIME,
340+
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
341+
builder.get_spec(),
342+
)
343+
.expect("should generate interop state");
331344
builder
332345
.genesis_state(genesis_state)
333346
.expect("should build state using recent genesis")
@@ -375,6 +388,7 @@ where
375388
mock_execution_layer: None,
376389
testing_slot_clock: None,
377390
validator_monitor_config: None,
391+
genesis_state_builder: None,
378392
import_all_data_columns: false,
379393
runtime,
380394
log,
@@ -560,6 +574,15 @@ where
560574
self
561575
}
562576

577+
pub fn with_genesis_state_builder(
578+
mut self,
579+
f: impl FnOnce(InteropGenesisBuilder<E>) -> InteropGenesisBuilder<E>,
580+
) -> Self {
581+
let builder = self.genesis_state_builder.take().unwrap_or_default();
582+
self.genesis_state_builder = Some(f(builder));
583+
self
584+
}
585+
563586
pub fn build(self) -> BeaconChainHarness<BaseHarnessType<E, Hot, Cold>> {
564587
let (shutdown_tx, shutdown_receiver) = futures::channel::mpsc::channel(1);
565588

beacon_node/beacon_chain/tests/rewards.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,38 @@ fn get_harness(spec: ChainSpec) -> BeaconChainHarness<EphemeralHarnessType<E>> {
3636
.keypairs(KEYPAIRS.to_vec())
3737
.fresh_ephemeral_store()
3838
.chain_config(chain_config)
39+
.mock_execution_layer()
40+
.build();
41+
42+
harness.advance_slot();
43+
44+
harness
45+
}
46+
47+
fn get_electra_harness(spec: ChainSpec) -> BeaconChainHarness<EphemeralHarnessType<E>> {
48+
let chain_config = ChainConfig {
49+
reconstruct_historic_states: true,
50+
..Default::default()
51+
};
52+
53+
let spec = Arc::new(spec);
54+
55+
let harness = BeaconChainHarness::builder(E::default())
56+
.spec(spec.clone())
57+
.keypairs(KEYPAIRS.to_vec())
58+
.with_genesis_state_builder(|builder| {
59+
builder.set_initial_balance_fn(Box::new(move |i| {
60+
// Use a variety of balances between min activation balance and max effective balance.
61+
let balance = spec.max_effective_balance_electra
62+
/ (i as u64 + 1)
63+
/ spec.effective_balance_increment
64+
* spec.effective_balance_increment;
65+
balance.max(spec.min_activation_balance)
66+
}))
67+
})
68+
.fresh_ephemeral_store()
69+
.chain_config(chain_config)
70+
.mock_execution_layer()
3971
.build();
4072

4173
harness.advance_slot();
@@ -560,6 +592,83 @@ async fn test_rewards_altair_inactivity_leak_justification_epoch() {
560592
assert_eq!(expected_balances, balances);
561593
}
562594

595+
#[tokio::test]
596+
async fn test_rewards_electra() {
597+
let spec = ForkName::Electra.make_genesis_spec(E::default_spec());
598+
let harness = get_electra_harness(spec.clone());
599+
let target_epoch = 0;
600+
601+
// advance until epoch N + 1 and get initial balances
602+
harness
603+
.extend_slots((E::slots_per_epoch() * (target_epoch + 1)) as usize)
604+
.await;
605+
let mut expected_balances = harness.get_current_state().balances().to_vec();
606+
607+
// advance until epoch N + 2 and build proposal rewards map
608+
let mut proposal_rewards_map = HashMap::new();
609+
let mut sync_committee_rewards_map = HashMap::new();
610+
for _ in 0..E::slots_per_epoch() {
611+
let state = harness.get_current_state();
612+
let slot = state.slot() + Slot::new(1);
613+
614+
// calculate beacon block rewards / penalties
615+
let ((signed_block, _maybe_blob_sidecars), mut state) =
616+
harness.make_block_return_pre_state(state, slot).await;
617+
let beacon_block_reward = harness
618+
.chain
619+
.compute_beacon_block_reward(signed_block.message(), &mut state)
620+
.unwrap();
621+
622+
let total_proposer_reward = proposal_rewards_map
623+
.entry(beacon_block_reward.proposer_index)
624+
.or_insert(0);
625+
*total_proposer_reward += beacon_block_reward.total as i64;
626+
627+
// calculate sync committee rewards / penalties
628+
let reward_payload = harness
629+
.chain
630+
.compute_sync_committee_rewards(signed_block.message(), &mut state)
631+
.unwrap();
632+
633+
for reward in reward_payload {
634+
let total_sync_reward = sync_committee_rewards_map
635+
.entry(reward.validator_index)
636+
.or_insert(0);
637+
*total_sync_reward += reward.reward;
638+
}
639+
640+
harness.extend_slots(1).await;
641+
}
642+
643+
// compute reward deltas for all validators in epoch N
644+
let StandardAttestationRewards {
645+
ideal_rewards,
646+
total_rewards,
647+
} = harness
648+
.chain
649+
.compute_attestation_rewards(Epoch::new(target_epoch), vec![])
650+
.unwrap();
651+
652+
// assert ideal rewards are greater than 0
653+
assert_eq!(
654+
ideal_rewards.len() as u64,
655+
spec.max_effective_balance_electra / spec.effective_balance_increment
656+
);
657+
assert!(ideal_rewards
658+
.iter()
659+
.all(|reward| reward.head > 0 && reward.target > 0 && reward.source > 0));
660+
661+
// apply attestation, proposal, and sync committee rewards and penalties to initial balances
662+
apply_attestation_rewards(&mut expected_balances, total_rewards);
663+
apply_other_rewards(&mut expected_balances, &proposal_rewards_map);
664+
apply_other_rewards(&mut expected_balances, &sync_committee_rewards_map);
665+
666+
// verify expected balances against actual balances
667+
let balances: Vec<u64> = harness.get_current_state().balances().to_vec();
668+
669+
assert_eq!(expected_balances, balances);
670+
}
671+
563672
#[tokio::test]
564673
async fn test_rewards_base_subset_only() {
565674
let spec = ForkName::Base.make_genesis_spec(E::default_spec());

0 commit comments

Comments
 (0)