Skip to content

Commit 4470f6d

Browse files
SIMD-0118: fix total_rewards for recalculation (#2780)
* Add new feature key * Wrap existing code with new feature * Extend test harness * Make test fail * Populate EpochRewards::total_rewards from PointValue * Remove superfluous struct field * Fixup tests
1 parent 5866bfb commit 4470f6d

File tree

10 files changed

+152
-56
lines changed

10 files changed

+152
-56
lines changed

programs/bpf_loader/src/syscalls/mod.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ use {
4242
enable_big_mod_exp_syscall, enable_get_epoch_stake_syscall,
4343
enable_partitioned_epoch_reward, enable_poseidon_syscall,
4444
error_on_syscall_bpf_function_hash_collisions, get_sysvar_syscall_enabled,
45-
last_restart_slot_sysvar, reject_callx_r10, remaining_compute_units_syscall_enabled,
45+
last_restart_slot_sysvar, partitioned_epoch_rewards_superfeature, reject_callx_r10,
46+
remaining_compute_units_syscall_enabled,
4647
},
4748
hash::{Hash, Hasher},
4849
instruction::{AccountMeta, InstructionError, ProcessedSiblingInstruction},
@@ -273,8 +274,9 @@ pub fn create_program_runtime_environment_v1<'a>(
273274
let blake3_syscall_enabled = feature_set.is_active(&blake3_syscall_enabled::id());
274275
let curve25519_syscall_enabled = feature_set.is_active(&curve25519_syscall_enabled::id());
275276
let disable_fees_sysvar = feature_set.is_active(&disable_fees_sysvar::id());
276-
let epoch_rewards_syscall_enabled =
277-
feature_set.is_active(&enable_partitioned_epoch_reward::id());
277+
let epoch_rewards_syscall_enabled = feature_set
278+
.is_active(&enable_partitioned_epoch_reward::id())
279+
|| feature_set.is_active(&partitioned_epoch_rewards_superfeature::id());
278280
let disable_deploy_of_alloc_free_syscall = reject_deployment_of_broken_elfs
279281
&& feature_set.is_active(&disable_deploy_of_alloc_free_syscall::id());
280282
let last_restart_slot_syscall_enabled = feature_set.is_active(&last_restart_slot_sysvar::id());

programs/stake/src/stake_instruction.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ declare_process_instruction!(Entrypoint, DEFAULT_COMPUTE_UNITS, |invoke_context|
6666
};
6767

6868
// The EpochRewards sysvar only exists after the
69-
// enable_partitioned_epoch_reward feature is activated. If it exists, check
70-
// the `active` field
69+
// partitioned_epoch_rewards_superfeature feature is activated. If it
70+
// exists, check the `active` field
7171
let epoch_rewards_active = invoke_context
7272
.get_sysvar_cache()
7373
.get_epoch_rewards()

rpc/src/rpc.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -610,12 +610,20 @@ impl JsonRpcRequestProcessor {
610610
// epoch
611611
let bank = self.get_bank_with_config(context_config)?;
612612

613-
// DO NOT CLEAN UP with feature_set::enable_partitioned_epoch_reward
613+
// DO NOT CLEAN UP with feature_set::partitioned_epoch_rewards_superfeature
614614
// This logic needs to be retained indefinitely to support historical
615615
// rewards before and after feature activation.
616616
let partitioned_epoch_reward_enabled_slot = bank
617617
.feature_set
618-
.activated_slot(&feature_set::enable_partitioned_epoch_reward::id());
618+
.activated_slot(&feature_set::partitioned_epoch_rewards_superfeature::id())
619+
.or_else(|| {
620+
// The order of these checks should not matter, since we will
621+
// not ever have both features active on a live cluster. This
622+
// check can be removed with
623+
// feature_set::enable_partitioned_epoch_reward
624+
bank.feature_set
625+
.activated_slot(&feature_set::enable_partitioned_epoch_reward::id())
626+
});
619627
let partitioned_epoch_reward_enabled = partitioned_epoch_reward_enabled_slot
620628
.map(|slot| slot <= first_confirmed_block_in_epoch)
621629
.unwrap_or(false);

runtime/src/bank/partitioned_epoch_rewards/calculation.rs

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ impl Bank {
5050
let CalculateRewardsAndDistributeVoteRewardsResult {
5151
total_rewards,
5252
distributed_rewards,
53-
total_points,
53+
point_value,
5454
stake_rewards_by_partition,
5555
} = self.calculate_rewards_and_distribute_vote_rewards(
5656
parent_epoch,
@@ -80,7 +80,7 @@ impl Bank {
8080
distributed_rewards,
8181
distribution_starting_block_height,
8282
num_partitions,
83-
total_points,
83+
point_value,
8484
);
8585

8686
datapoint_info!(
@@ -105,12 +105,11 @@ impl Bank {
105105
vote_account_rewards,
106106
stake_rewards_by_partition,
107107
old_vote_balance_and_staked,
108-
validator_rewards,
109108
validator_rate,
110109
foundation_rate,
111110
prev_epoch_duration_in_years,
112111
capitalization,
113-
total_points,
112+
point_value,
114113
} = self.calculate_rewards_for_partitioning(
115114
prev_epoch,
116115
reward_calc_tracer,
@@ -136,11 +135,11 @@ impl Bank {
136135
self.assert_validator_rewards_paid(validator_rewards_paid);
137136

138137
// verify that we didn't pay any more than we expected to
139-
assert!(validator_rewards >= validator_rewards_paid + total_stake_rewards_lamports);
138+
assert!(point_value.rewards >= validator_rewards_paid + total_stake_rewards_lamports);
140139

141140
info!(
142141
"distributed vote rewards: {} out of {}, remaining {}",
143-
validator_rewards_paid, validator_rewards, total_stake_rewards_lamports
142+
validator_rewards_paid, point_value.rewards, total_stake_rewards_lamports
144143
);
145144

146145
let (num_stake_accounts, num_vote_accounts) = {
@@ -179,7 +178,7 @@ impl Bank {
179178
CalculateRewardsAndDistributeVoteRewardsResult {
180179
total_rewards: validator_rewards_paid + total_stake_rewards_lamports,
181180
distributed_rewards: validator_rewards_paid,
182-
total_points,
181+
point_value,
183182
stake_rewards_by_partition,
184183
}
185184
}
@@ -231,7 +230,7 @@ impl Bank {
231230
let CalculateValidatorRewardsResult {
232231
vote_rewards_accounts: vote_account_rewards,
233232
stake_reward_calculation: mut stake_rewards,
234-
total_points,
233+
point_value,
235234
} = self
236235
.calculate_validator_rewards(
237236
prev_epoch,
@@ -260,12 +259,11 @@ impl Bank {
260259
total_stake_rewards_lamports: stake_rewards.total_stake_rewards_lamports,
261260
},
262261
old_vote_balance_and_staked,
263-
validator_rewards,
264262
validator_rate,
265263
foundation_rate,
266264
prev_epoch_duration_in_years,
267265
capitalization,
268-
total_points,
266+
point_value,
269267
}
270268
}
271269

@@ -288,20 +286,19 @@ impl Bank {
288286
metrics,
289287
)
290288
.map(|point_value| {
291-
let total_points = point_value.points;
292289
let (vote_rewards_accounts, stake_reward_calculation) = self
293290
.calculate_stake_vote_rewards(
294291
&reward_calculate_param,
295292
rewarded_epoch,
296-
point_value,
293+
point_value.clone(),
297294
thread_pool,
298295
reward_calc_tracer,
299296
metrics,
300297
);
301298
CalculateValidatorRewardsResult {
302299
vote_rewards_accounts,
303300
stake_reward_calculation,
304-
total_points,
301+
point_value,
305302
}
306303
})
307304
}
@@ -601,7 +598,8 @@ mod tests {
601598
null_tracer,
602599
partitioned_epoch_rewards::{
603600
tests::{
604-
create_default_reward_bank, create_reward_bank, RewardBank, SLOTS_PER_EPOCH,
601+
create_default_reward_bank, create_reward_bank,
602+
create_reward_bank_with_specific_stakes, RewardBank, SLOTS_PER_EPOCH,
605603
},
606604
EpochRewardStatus, StartBlockHeightAndRewards,
607605
},
@@ -988,11 +986,14 @@ mod tests {
988986

989987
#[test]
990988
fn test_recalculate_partitioned_rewards() {
991-
let expected_num_delegations = 4;
989+
let expected_num_delegations = 3;
992990
let num_rewards_per_block = 2;
993991
// Distribute 4 rewards over 2 blocks
994-
let RewardBank { bank, .. } = create_reward_bank(
995-
expected_num_delegations,
992+
let mut stakes = vec![2_000_000_000; expected_num_delegations];
993+
// Add stake large enough to be affected by total-rewards discrepancy
994+
stakes.push(40_000_000_000);
995+
let RewardBank { bank, .. } = create_reward_bank_with_specific_stakes(
996+
stakes,
996997
num_rewards_per_block,
997998
SLOTS_PER_EPOCH - 1,
998999
);
@@ -1012,6 +1013,7 @@ mod tests {
10121013
stake_rewards_by_partition: expected_stake_rewards,
10131014
..
10141015
},
1016+
point_value,
10151017
..
10161018
} = bank.calculate_rewards_for_partitioning(
10171019
rewarded_epoch,
@@ -1035,6 +1037,9 @@ mod tests {
10351037
assert_eq!(expected_stake_rewards.len(), recalculated_rewards.len());
10361038
compare_stake_rewards(&expected_stake_rewards, recalculated_rewards);
10371039

1040+
let sysvar = bank.get_epoch_rewards_sysvar();
1041+
assert_eq!(point_value.rewards, sysvar.total_rewards);
1042+
10381043
// Advance to first distribution slot
10391044
let mut bank =
10401045
Bank::new_from_parent(Arc::new(bank), &Pubkey::default(), SLOTS_PER_EPOCH + 1);

runtime/src/bank/partitioned_epoch_rewards/distribution.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ mod tests {
255255
},
256256
sysvar,
257257
},
258-
solana_stake_program::stake_state,
258+
solana_stake_program::{points::PointValue, stake_state},
259259
solana_vote_program::vote_state,
260260
};
261261

@@ -349,13 +349,22 @@ mod tests {
349349
create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
350350
genesis_config.epoch_schedule = EpochSchedule::custom(432000, 432000, false);
351351
let mut bank = Bank::new_for_tests(&genesis_config);
352-
bank.activate_feature(&feature_set::enable_partitioned_epoch_reward::id());
352+
bank.activate_feature(&feature_set::partitioned_epoch_rewards_superfeature::id());
353353

354354
// Set up epoch_rewards sysvar with rewards with 1e9 lamports to distribute.
355355
let total_rewards = 1_000_000_000;
356356
let num_partitions = 2; // num_partitions is arbitrary and unimportant for this test
357357
let total_points = (total_rewards * 42) as u128; // total_points is arbitrary for the purposes of this test
358-
bank.create_epoch_rewards_sysvar(total_rewards, 0, 42, num_partitions, total_points);
358+
bank.create_epoch_rewards_sysvar(
359+
total_rewards,
360+
0,
361+
42,
362+
num_partitions,
363+
PointValue {
364+
rewards: total_rewards,
365+
points: total_points,
366+
},
367+
);
359368
let pre_epoch_rewards_account = bank.get_account(&sysvar::epoch_rewards::id()).unwrap();
360369
let expected_balance =
361370
bank.get_minimum_balance_for_rent_exemption(pre_epoch_rewards_account.data().len());

runtime/src/bank/partitioned_epoch_rewards/mod.rs

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use {
1818
reward_info::RewardInfo,
1919
stake::state::{Delegation, Stake, StakeStateV2},
2020
},
21+
solana_stake_program::points::PointValue,
2122
solana_vote::vote_account::VoteAccounts,
2223
std::sync::Arc,
2324
};
@@ -95,11 +96,24 @@ struct StakeRewardCalculation {
9596
total_stake_rewards_lamports: u64,
9697
}
9798

98-
#[derive(Debug, Default)]
99+
#[derive(Debug)]
99100
struct CalculateValidatorRewardsResult {
100101
vote_rewards_accounts: VoteRewardsAccounts,
101102
stake_reward_calculation: StakeRewardCalculation,
102-
total_points: u128,
103+
point_value: PointValue,
104+
}
105+
106+
impl Default for CalculateValidatorRewardsResult {
107+
fn default() -> Self {
108+
Self {
109+
vote_rewards_accounts: VoteRewardsAccounts::default(),
110+
stake_reward_calculation: StakeRewardCalculation::default(),
111+
point_value: PointValue {
112+
points: 0,
113+
rewards: 0,
114+
},
115+
}
116+
}
103117
}
104118

105119
/// hold reward calc info to avoid recalculation across functions
@@ -116,12 +130,11 @@ pub(super) struct PartitionedRewardsCalculation {
116130
pub(super) vote_account_rewards: VoteRewardsAccounts,
117131
pub(super) stake_rewards_by_partition: StakeRewardCalculationPartitioned,
118132
pub(super) old_vote_balance_and_staked: u64,
119-
pub(super) validator_rewards: u64,
120133
pub(super) validator_rate: f64,
121134
pub(super) foundation_rate: f64,
122135
pub(super) prev_epoch_duration_in_years: f64,
123136
pub(super) capitalization: u64,
124-
total_points: u128,
137+
point_value: PointValue,
125138
}
126139

127140
/// result of calculating the stake rewards at beginning of new epoch
@@ -133,14 +146,16 @@ pub(super) struct StakeRewardCalculationPartitioned {
133146
}
134147

135148
pub(super) struct CalculateRewardsAndDistributeVoteRewardsResult {
136-
/// total rewards for the epoch (including both vote rewards and stake rewards)
149+
/// total rewards to be distributed in the epoch (including both vote
150+
/// rewards and stake rewards)
137151
pub(super) total_rewards: u64,
138152
/// distributed vote rewards
139153
pub(super) distributed_rewards: u64,
140-
/// total rewards points calculated for the current epoch, where points
154+
/// total rewards and points calculated for the current epoch, where points
141155
/// equals the sum of (delegated stake * credits observed) for all
142-
/// delegations
143-
pub(super) total_points: u128,
156+
/// delegations and rewards are the lamports to split across all stake and
157+
/// vote accounts
158+
pub(super) point_value: PointValue,
144159
/// stake rewards that still need to be distributed, grouped by partition
145160
pub(super) stake_rewards_by_partition: Vec<PartitionedStakeRewards>,
146161
}
@@ -180,6 +195,9 @@ impl Bank {
180195
pub(super) fn is_partitioned_rewards_feature_enabled(&self) -> bool {
181196
self.feature_set
182197
.is_active(&feature_set::enable_partitioned_epoch_reward::id())
198+
|| self
199+
.feature_set
200+
.is_active(&feature_set::partitioned_epoch_rewards_superfeature::id())
183201
}
184202

185203
pub(crate) fn set_epoch_reward_status_active(
@@ -347,17 +365,25 @@ mod tests {
347365
stake_account_stores_per_block: u64,
348366
advance_num_slots: u64,
349367
) -> RewardBank {
350-
let validator_keypairs = (0..expected_num_delegations)
368+
create_reward_bank_with_specific_stakes(
369+
vec![2_000_000_000; expected_num_delegations],
370+
stake_account_stores_per_block,
371+
advance_num_slots,
372+
)
373+
}
374+
375+
pub(super) fn create_reward_bank_with_specific_stakes(
376+
stakes: Vec<u64>,
377+
stake_account_stores_per_block: u64,
378+
advance_num_slots: u64,
379+
) -> RewardBank {
380+
let validator_keypairs = (0..stakes.len())
351381
.map(|_| ValidatorVoteKeypairs::new_rand())
352382
.collect::<Vec<_>>();
353383

354384
let GenesisConfigInfo {
355385
mut genesis_config, ..
356-
} = create_genesis_config_with_vote_accounts(
357-
1_000_000_000,
358-
&validator_keypairs,
359-
vec![2_000_000_000; expected_num_delegations],
360-
);
386+
} = create_genesis_config_with_vote_accounts(1_000_000_000, &validator_keypairs, stakes);
361387
genesis_config.epoch_schedule = EpochSchedule::new(SLOTS_PER_EPOCH);
362388

363389
let mut accounts_db_config: AccountsDbConfig = ACCOUNTS_DB_CONFIG_FOR_TESTING.clone();
@@ -456,7 +482,7 @@ mod tests {
456482

457483
let mut bank = Bank::new_for_tests(&genesis_config);
458484
assert!(!bank.is_partitioned_rewards_feature_enabled());
459-
bank.activate_feature(&feature_set::enable_partitioned_epoch_reward::id());
485+
bank.activate_feature(&feature_set::partitioned_epoch_rewards_superfeature::id());
460486
assert!(bank.is_partitioned_rewards_feature_enabled());
461487
}
462488

@@ -950,6 +976,9 @@ mod tests {
950976
genesis_config
951977
.accounts
952978
.remove(&feature_set::enable_partitioned_epoch_reward::id());
979+
genesis_config
980+
.accounts
981+
.remove(&feature_set::partitioned_epoch_rewards_superfeature::id());
953982

954983
let bank = Bank::new_for_tests(&genesis_config);
955984

0 commit comments

Comments
 (0)