Skip to content

Commit e61e92b

Browse files
committed
Merge remote-tracking branch 'origin/stable' into unstable
2 parents 9f4b0cd + 54f7bc5 commit e61e92b

File tree

5 files changed

+194
-10
lines changed

5 files changed

+194
-10
lines changed

.github/mergify.yml

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,37 @@
1+
pull_request_rules:
2+
- name: Ask to resolve conflict
3+
conditions:
4+
- conflict
5+
- -author=dependabot[bot]
6+
- or:
7+
- -draft # Don't report conflicts on regular draft.
8+
- and: # Do report conflicts on draft that are scheduled for the next major release.
9+
- draft
10+
- milestone~=v[0-9]\.[0-9]{2}
11+
actions:
12+
comment:
13+
message: This pull request has merge conflicts. Could you please resolve them
14+
@{{author}}? 🙏
15+
16+
- name: Approve trivial maintainer PRs
17+
conditions:
18+
- base!=stable
19+
- label=trivial
20+
- author=@sigp/lighthouse
21+
- -conflict
22+
actions:
23+
review:
24+
type: APPROVE
25+
26+
- name: Add ready-to-merge labeled PRs to merge queue
27+
conditions:
28+
# All branch protection rules are implicit: https://docs.mergify.com/conditions/#about-branch-protection
29+
- base!=stable
30+
- label=ready-for-merge
31+
- label!=do-not-merge
32+
actions:
33+
queue:
34+
135
queue_rules:
236
- name: default
337
batch_size: 8
@@ -6,14 +40,16 @@ queue_rules:
640
merge_method: squash
741
commit_message_template: |
842
{{ title }} (#{{ number }})
9-
10-
{% for commit in commits %}
11-
* {{ commit.commit_message }}
12-
{% endfor %}
43+
44+
{{ body | get_section("## Issue Addressed", "") }}
45+
46+
47+
{{ body | get_section("## Proposed Changes", "") }}
1348
queue_conditions:
1449
- "#approved-reviews-by >= 1"
1550
- "check-success=license/cla"
1651
- "check-success=target-branch-check"
52+
- "label!=do-not-merge"
1753
merge_conditions:
1854
- "check-success=test-suite-success"
1955
- "check-success=local-testnet-success"

beacon_node/beacon_chain/src/beacon_block_reward.rs

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

@@ -157,7 +157,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
157157
state
158158
.get_validator(attester_index as usize)?
159159
.effective_balance
160-
.safe_div(self.spec.whistleblower_reward_quotient)?,
160+
.safe_div(self.spec.whistleblower_reward_quotient_for_state(state))?,
161161
)?;
162162
}
163163
}

beacon_node/beacon_chain/tests/rewards.rs

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

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

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

beacon_node/http_api/tests/tests.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6363,6 +6363,34 @@ impl ApiTester {
63636363

63646364
assert_eq!(result.execution_optimistic, Some(true));
63656365
}
6366+
6367+
async fn test_get_beacon_rewards_blocks_at_head(&self) -> StandardBlockReward {
6368+
self.client
6369+
.get_beacon_rewards_blocks(CoreBlockId::Head)
6370+
.await
6371+
.unwrap()
6372+
.data
6373+
}
6374+
6375+
async fn test_beacon_block_rewards_electra(self) -> Self {
6376+
for _ in 0..E::slots_per_epoch() {
6377+
let state = self.harness.get_current_state();
6378+
let slot = state.slot() + Slot::new(1);
6379+
// calculate beacon block rewards / penalties
6380+
let ((signed_block, _maybe_blob_sidecars), mut state) =
6381+
self.harness.make_block_return_pre_state(state, slot).await;
6382+
6383+
let beacon_block_reward = self
6384+
.harness
6385+
.chain
6386+
.compute_beacon_block_reward(signed_block.message(), &mut state)
6387+
.unwrap();
6388+
self.harness.extend_slots(1).await;
6389+
let api_beacon_block_reward = self.test_get_beacon_rewards_blocks_at_head().await;
6390+
assert_eq!(beacon_block_reward, api_beacon_block_reward);
6391+
}
6392+
self
6393+
}
63666394
}
63676395

63686396
async fn poll_events<S: Stream<Item = Result<EventKind<E>, eth2::Error>> + Unpin, E: EthSpec>(
@@ -7502,3 +7530,17 @@ async fn expected_withdrawals_valid_capella() {
75027530
.test_get_expected_withdrawals_capella()
75037531
.await;
75047532
}
7533+
7534+
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
7535+
async fn get_beacon_rewards_blocks_electra() {
7536+
let mut config = ApiTesterConfig::default();
7537+
config.spec.altair_fork_epoch = Some(Epoch::new(0));
7538+
config.spec.bellatrix_fork_epoch = Some(Epoch::new(0));
7539+
config.spec.capella_fork_epoch = Some(Epoch::new(0));
7540+
config.spec.deneb_fork_epoch = Some(Epoch::new(0));
7541+
config.spec.electra_fork_epoch = Some(Epoch::new(0));
7542+
ApiTester::new_from_config(config)
7543+
.await
7544+
.test_beacon_block_rewards_electra()
7545+
.await;
7546+
}

consensus/state_processing/src/per_block_processing.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ pub fn get_expected_withdrawals<E: EthSpec>(
517517
let epoch = state.current_epoch();
518518
let mut withdrawal_index = state.next_withdrawal_index()?;
519519
let mut validator_index = state.next_withdrawal_validator_index()?;
520-
let mut withdrawals = vec![];
520+
let mut withdrawals = Vec::<Withdrawal>::with_capacity(E::max_withdrawals_per_payload());
521521
let fork_name = state.fork_name_unchecked();
522522

523523
// [New in Electra:EIP7251]
@@ -532,19 +532,27 @@ pub fn get_expected_withdrawals<E: EthSpec>(
532532
break;
533533
}
534534

535-
let withdrawal_balance = state.get_balance(withdrawal.validator_index as usize)?;
536535
let validator = state.get_validator(withdrawal.validator_index as usize)?;
537536

538537
let has_sufficient_effective_balance =
539538
validator.effective_balance >= spec.min_activation_balance;
540-
let has_excess_balance = withdrawal_balance > spec.min_activation_balance;
539+
let total_withdrawn = withdrawals
540+
.iter()
541+
.filter_map(|w| {
542+
(w.validator_index == withdrawal.validator_index).then_some(w.amount)
543+
})
544+
.safe_sum()?;
545+
let balance = state
546+
.get_balance(withdrawal.validator_index as usize)?
547+
.safe_sub(total_withdrawn)?;
548+
let has_excess_balance = balance > spec.min_activation_balance;
541549

542550
if validator.exit_epoch == spec.far_future_epoch
543551
&& has_sufficient_effective_balance
544552
&& has_excess_balance
545553
{
546554
let withdrawable_balance = std::cmp::min(
547-
withdrawal_balance.safe_sub(spec.min_activation_balance)?,
555+
balance.safe_sub(spec.min_activation_balance)?,
548556
withdrawal.amount,
549557
);
550558
withdrawals.push(Withdrawal {

0 commit comments

Comments
 (0)