Skip to content

Commit 98eb135

Browse files
authored
Move reward partition hashing to reward payout block (#5613)
move reward partition to later block
1 parent 06b4ba1 commit 98eb135

File tree

4 files changed

+217
-81
lines changed

4 files changed

+217
-81
lines changed

runtime/src/bank/partitioned_epoch_rewards/calculation.rs

+56-44
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use {
33
epoch_rewards_hasher::hash_rewards_into_partitions, Bank,
44
CalculateRewardsAndDistributeVoteRewardsResult, CalculateValidatorRewardsResult,
55
EpochRewardCalculateParamInfo, PartitionedRewardsCalculation, PartitionedStakeReward,
6-
PartitionedStakeRewards, StakeRewardCalculation, StakeRewardCalculationPartitioned,
7-
VoteRewardsAccounts, REWARD_CALCULATION_NUM_BLOCKS,
6+
PartitionedStakeRewards, StakeRewardCalculation, VoteRewardsAccounts,
7+
REWARD_CALCULATION_NUM_BLOCKS,
88
},
99
crate::{
1010
bank::{
@@ -50,7 +50,7 @@ impl Bank {
5050
let CalculateRewardsAndDistributeVoteRewardsResult {
5151
distributed_rewards,
5252
point_value,
53-
stake_rewards_by_partition,
53+
stake_rewards,
5454
} = self.calculate_rewards_and_distribute_vote_rewards(
5555
parent_epoch,
5656
reward_calc_tracer,
@@ -62,12 +62,9 @@ impl Bank {
6262
let distribution_starting_block_height =
6363
self.block_height() + REWARD_CALCULATION_NUM_BLOCKS;
6464

65-
let num_partitions = stake_rewards_by_partition.len() as u64;
65+
let num_partitions = self.get_reward_distribution_num_blocks(&stake_rewards);
6666

67-
self.set_epoch_reward_status_active(
68-
distribution_starting_block_height,
69-
stake_rewards_by_partition,
70-
);
67+
self.set_epoch_reward_status_calculated(distribution_starting_block_height, stake_rewards);
7168

7269
self.create_epoch_rewards_sysvar(
7370
distributed_rewards,
@@ -96,7 +93,7 @@ impl Bank {
9693
) -> CalculateRewardsAndDistributeVoteRewardsResult {
9794
let PartitionedRewardsCalculation {
9895
vote_account_rewards,
99-
stake_rewards_by_partition,
96+
stake_rewards,
10097
validator_rate,
10198
foundation_rate,
10299
prev_epoch_duration_in_years,
@@ -114,10 +111,10 @@ impl Bank {
114111
// update reward history of JUST vote_rewards, stake_rewards is vec![] here
115112
self.update_reward_history(vec![], vote_rewards);
116113

117-
let StakeRewardCalculationPartitioned {
118-
stake_rewards_by_partition,
114+
let StakeRewardCalculation {
115+
stake_rewards,
119116
total_stake_rewards_lamports,
120-
} = stake_rewards_by_partition;
117+
} = stake_rewards;
121118

122119
// verify that we didn't pay any more than we expected to
123120
assert!(point_value.rewards >= total_vote_rewards + total_stake_rewards_lamports);
@@ -161,7 +158,7 @@ impl Bank {
161158
CalculateRewardsAndDistributeVoteRewardsResult {
162159
distributed_rewards: total_vote_rewards,
163160
point_value,
164-
stake_rewards_by_partition,
161+
stake_rewards,
165162
}
166163
}
167164

@@ -211,22 +208,10 @@ impl Bank {
211208
)
212209
.unwrap_or_default();
213210

214-
let num_partitions = self.get_reward_distribution_num_blocks(&stake_rewards.stake_rewards);
215-
let parent_blockhash = self
216-
.parent()
217-
.expect("Partitioned rewards calculation must still have access to parent Bank.")
218-
.last_blockhash();
219-
let (stake_rewards_by_partition, hash_us) = measure_us!(hash_rewards_into_partitions(
220-
std::mem::take(&mut stake_rewards.stake_rewards),
221-
&parent_blockhash,
222-
num_partitions as usize,
223-
));
224-
metrics.hash_partition_rewards_us += hash_us;
225-
226211
PartitionedRewardsCalculation {
227212
vote_account_rewards,
228-
stake_rewards_by_partition: StakeRewardCalculationPartitioned {
229-
stake_rewards_by_partition,
213+
stake_rewards: StakeRewardCalculation {
214+
stake_rewards: std::mem::take(&mut stake_rewards.stake_rewards),
230215
total_stake_rewards_lamports: stake_rewards.total_stake_rewards_lamports,
231216
},
232217
validator_rate,
@@ -488,7 +473,7 @@ impl Bank {
488473
reward_calc_tracer,
489474
thread_pool,
490475
);
491-
self.set_epoch_reward_status_active(
476+
self.set_epoch_reward_status_partitioned(
492477
epoch_rewards_sysvar.distribution_starting_block_height,
493478
stake_rewards_by_partition,
494479
);
@@ -551,7 +536,7 @@ mod tests {
551536
create_default_reward_bank, create_reward_bank,
552537
create_reward_bank_with_specific_stakes, RewardBank, SLOTS_PER_EPOCH,
553538
},
554-
EpochRewardStatus, StartBlockHeightAndRewards,
539+
EpochRewardStatus, StartBlockHeightAndPartitionedRewards,
555540
},
556541
tests::create_genesis_config,
557542
VoteReward,
@@ -849,9 +834,9 @@ mod tests {
849834
let thread_pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
850835
let mut rewards_metrics = RewardsMetrics::default();
851836
let PartitionedRewardsCalculation {
852-
stake_rewards_by_partition:
853-
StakeRewardCalculationPartitioned {
854-
stake_rewards_by_partition: expected_stake_rewards,
837+
stake_rewards:
838+
StakeRewardCalculation {
839+
stake_rewards: expected_stake_rewards,
855840
..
856841
},
857842
..
@@ -865,8 +850,18 @@ mod tests {
865850
let epoch_rewards_sysvar = bank.get_epoch_rewards_sysvar();
866851
let recalculated_rewards =
867852
bank.recalculate_stake_rewards(&epoch_rewards_sysvar, null_tracer(), &thread_pool);
868-
assert_eq!(expected_stake_rewards.len(), recalculated_rewards.len());
869-
compare_stake_rewards(&expected_stake_rewards, &recalculated_rewards);
853+
854+
let expected_stake_rewards_partitioned = hash_rewards_into_partitions(
855+
expected_stake_rewards,
856+
&epoch_rewards_sysvar.parent_blockhash,
857+
epoch_rewards_sysvar.num_partitions as usize,
858+
);
859+
860+
assert_eq!(
861+
expected_stake_rewards_partitioned.len(),
862+
recalculated_rewards.len()
863+
);
864+
compare_stake_rewards(&expected_stake_rewards_partitioned, &recalculated_rewards);
870865

871866
// Advance to first distribution block, ie. child block of the epoch
872867
// boundary; slot is advanced 2 to demonstrate that distribution works
@@ -877,15 +872,18 @@ mod tests {
877872
let epoch_rewards_sysvar = bank.get_epoch_rewards_sysvar();
878873
let recalculated_rewards =
879874
bank.recalculate_stake_rewards(&epoch_rewards_sysvar, null_tracer(), &thread_pool);
880-
assert_eq!(expected_stake_rewards.len(), recalculated_rewards.len());
875+
assert_eq!(
876+
expected_stake_rewards_partitioned.len(),
877+
recalculated_rewards.len()
878+
);
881879
// First partition has already been distributed, so recalculation
882880
// returns 0 rewards
883881
assert_eq!(recalculated_rewards[0].len(), 0);
884882
let starting_index = (bank.block_height() + 1
885883
- epoch_rewards_sysvar.distribution_starting_block_height)
886884
as usize;
887885
compare_stake_rewards(
888-
&expected_stake_rewards[starting_index..],
886+
&expected_stake_rewards_partitioned[starting_index..],
889887
&recalculated_rewards[starting_index..],
890888
);
891889

@@ -914,9 +912,9 @@ mod tests {
914912
let thread_pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
915913
let mut rewards_metrics = RewardsMetrics::default();
916914
let PartitionedRewardsCalculation {
917-
stake_rewards_by_partition:
918-
StakeRewardCalculationPartitioned {
919-
stake_rewards_by_partition: expected_stake_rewards,
915+
stake_rewards:
916+
StakeRewardCalculation {
917+
stake_rewards: expected_stake_rewards,
920918
..
921919
},
922920
..
@@ -928,6 +926,12 @@ mod tests {
928926
);
929927

930928
let epoch_rewards_sysvar = bank.get_epoch_rewards_sysvar();
929+
let expected_stake_rewards = hash_rewards_into_partitions(
930+
expected_stake_rewards,
931+
&epoch_rewards_sysvar.parent_blockhash,
932+
epoch_rewards_sysvar.num_partitions as usize,
933+
);
934+
931935
let recalculated_rewards =
932936
bank.recalculate_stake_rewards(&epoch_rewards_sysvar, null_tracer(), &thread_pool);
933937
assert_eq!(expected_stake_rewards.len(), recalculated_rewards.len());
@@ -968,9 +972,9 @@ mod tests {
968972
let thread_pool = ThreadPoolBuilder::new().num_threads(1).build().unwrap();
969973
let mut rewards_metrics = RewardsMetrics::default();
970974
let PartitionedRewardsCalculation {
971-
stake_rewards_by_partition:
972-
StakeRewardCalculationPartitioned {
973-
stake_rewards_by_partition: expected_stake_rewards,
975+
stake_rewards:
976+
StakeRewardCalculation {
977+
stake_rewards: expected_stake_rewards,
974978
..
975979
},
976980
point_value,
@@ -983,7 +987,7 @@ mod tests {
983987
);
984988

985989
bank.recalculate_partitioned_rewards(null_tracer(), &thread_pool);
986-
let EpochRewardStatus::Active(StartBlockHeightAndRewards {
990+
let EpochRewardStatus::Partitioned(StartBlockHeightAndPartitionedRewards {
987991
distribution_starting_block_height,
988992
stake_rewards_by_partition: ref recalculated_rewards,
989993
}) = bank.epoch_reward_status
@@ -994,6 +998,14 @@ mod tests {
994998
expected_starting_block_height,
995999
distribution_starting_block_height
9961000
);
1001+
1002+
let epoch_rewards_sysvar = bank.get_epoch_rewards_sysvar();
1003+
let expected_stake_rewards = hash_rewards_into_partitions(
1004+
expected_stake_rewards,
1005+
&epoch_rewards_sysvar.parent_blockhash,
1006+
epoch_rewards_sysvar.num_partitions as usize,
1007+
);
1008+
9971009
assert_eq!(expected_stake_rewards.len(), recalculated_rewards.len());
9981010
compare_stake_rewards(&expected_stake_rewards, recalculated_rewards);
9991011

@@ -1005,7 +1017,7 @@ mod tests {
10051017
Bank::new_from_parent(Arc::new(bank), &Pubkey::default(), SLOTS_PER_EPOCH + 1);
10061018

10071019
bank.recalculate_partitioned_rewards(null_tracer(), &thread_pool);
1008-
let EpochRewardStatus::Active(StartBlockHeightAndRewards {
1020+
let EpochRewardStatus::Partitioned(StartBlockHeightAndPartitionedRewards {
10091021
distribution_starting_block_height,
10101022
stake_rewards_by_partition: ref recalculated_rewards,
10111023
}) = bank.epoch_reward_status

runtime/src/bank/partitioned_epoch_rewards/distribution.rs

+69-15
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ use {
33
Bank, EpochRewardStatus, PartitionedStakeReward, PartitionedStakeRewards, StakeRewards,
44
},
55
crate::{
6-
bank::metrics::{report_partitioned_reward_metrics, RewardsStoreMetrics},
6+
bank::{
7+
metrics::{report_partitioned_reward_metrics, RewardsStoreMetrics},
8+
partitioned_epoch_rewards::StartBlockHeightAndPartitionedRewards,
9+
},
710
stake_account::StakeAccount,
811
},
912
log::error,
@@ -42,27 +45,81 @@ struct DistributionResults {
4245
impl Bank {
4346
/// Process reward distribution for the block if it is inside reward interval.
4447
pub(in crate::bank) fn distribute_partitioned_epoch_rewards(&mut self) {
45-
let EpochRewardStatus::Active(status) = &self.epoch_reward_status else {
46-
return;
48+
let distribution_starting_block_height = match &self.epoch_reward_status {
49+
EpochRewardStatus::Inactive => {
50+
// epoch rewards is inactive, no rewards to distribute
51+
return;
52+
}
53+
EpochRewardStatus::Calculated(status) => status.distribution_starting_block_height,
54+
EpochRewardStatus::Partitioned(status) => status.distribution_starting_block_height,
4755
};
4856

4957
let height = self.block_height();
50-
let distribution_starting_block_height = status.distribution_starting_block_height;
58+
if height < distribution_starting_block_height {
59+
return;
60+
}
61+
62+
let (stake_rewards_by_partition, update_to_partitioned) = match &self.epoch_reward_status {
63+
EpochRewardStatus::Inactive => {
64+
// epoch rewards is inactive, no rewards to distribute
65+
unreachable!("shouldn't reach here");
66+
}
67+
EpochRewardStatus::Calculated(status) => {
68+
// epoch rewards have been calculated, but not yet partitioned.
69+
// so partition them now.
70+
// This should happen only once immediately on the first rewards distribution block, after reward calculation block.
71+
let epoch_rewards_sysvar = self.get_epoch_rewards_sysvar();
72+
let (stake_rewards_by_partition, partition_us) = measure_us!(status.do_partition(
73+
&epoch_rewards_sysvar.parent_blockhash,
74+
epoch_rewards_sysvar.num_partitions as usize,
75+
));
76+
datapoint_info!(
77+
"epoch-rewards-status-update",
78+
("slot", self.slot(), i64),
79+
("block_height", height, i64),
80+
("partition_us", partition_us, i64),
81+
(
82+
"distribution_starting_block_height",
83+
distribution_starting_block_height,
84+
i64
85+
),
86+
);
87+
88+
(stake_rewards_by_partition, true)
89+
}
90+
EpochRewardStatus::Partitioned(status) => (
91+
// epoch rewards have been partitioned, so use the partitioned rewards
92+
status.stake_rewards_by_partition.clone(),
93+
false,
94+
),
95+
};
96+
5197
let distribution_end_exclusive =
52-
distribution_starting_block_height + status.stake_rewards_by_partition.len() as u64;
98+
distribution_starting_block_height + stake_rewards_by_partition.len() as u64;
99+
53100
assert!(
54101
self.epoch_schedule.get_slots_in_epoch(self.epoch)
55-
> status.stake_rewards_by_partition.len() as u64
102+
> stake_rewards_by_partition.len() as u64
56103
);
57104

58105
if height >= distribution_starting_block_height && height < distribution_end_exclusive {
59106
let partition_index = height - distribution_starting_block_height;
107+
60108
self.distribute_epoch_rewards_in_partition(
61-
&status.stake_rewards_by_partition,
109+
&stake_rewards_by_partition,
62110
partition_index,
63111
);
64112
}
65113

114+
if update_to_partitioned {
115+
// update epoch reward status to partitioned
116+
self.epoch_reward_status =
117+
EpochRewardStatus::Partitioned(StartBlockHeightAndPartitionedRewards {
118+
distribution_starting_block_height,
119+
stake_rewards_by_partition,
120+
});
121+
}
122+
66123
if height.saturating_add(1) >= distribution_end_exclusive {
67124
datapoint_info!(
68125
"epoch-rewards-status-update",
@@ -78,7 +135,7 @@ impl Bank {
78135

79136
assert!(matches!(
80137
self.epoch_reward_status,
81-
EpochRewardStatus::Active(_)
138+
EpochRewardStatus::Partitioned(_)
82139
));
83140
self.epoch_reward_status = EpochRewardStatus::Inactive;
84141
self.set_epoch_rewards_sysvar_to_inactive();
@@ -280,7 +337,7 @@ mod tests {
280337
let stake_rewards =
281338
hash_rewards_into_partitions(stake_rewards, &Hash::new_from_array([1; 32]), 2);
282339

283-
bank.set_epoch_reward_status_active(
340+
bank.set_epoch_reward_status_partitioned(
284341
bank.block_height() + REWARD_CALCULATION_NUM_BLOCKS,
285342
stake_rewards,
286343
);
@@ -306,10 +363,7 @@ mod tests {
306363
bank.epoch_schedule().slots_per_epoch as usize + 1,
307364
);
308365

309-
bank.set_epoch_reward_status_active(
310-
bank.block_height() + REWARD_CALCULATION_NUM_BLOCKS,
311-
stake_rewards,
312-
);
366+
bank.set_epoch_reward_status_partitioned(bank.block_height(), stake_rewards);
313367

314368
bank.distribute_partitioned_epoch_rewards();
315369
}
@@ -319,7 +373,7 @@ mod tests {
319373
let (genesis_config, _mint_keypair) = create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
320374
let mut bank = Bank::new_for_tests(&genesis_config);
321375

322-
bank.set_epoch_reward_status_active(
376+
bank.set_epoch_reward_status_partitioned(
323377
bank.block_height() + REWARD_CALCULATION_NUM_BLOCKS,
324378
vec![],
325379
);
@@ -437,7 +491,7 @@ mod tests {
437491

438492
let stake_rewards_bucket =
439493
hash_rewards_into_partitions(stake_rewards, &Hash::new_from_array([1; 32]), 100);
440-
bank.set_epoch_reward_status_active(
494+
bank.set_epoch_reward_status_partitioned(
441495
bank.block_height() + REWARD_CALCULATION_NUM_BLOCKS,
442496
stake_rewards_bucket.clone(),
443497
);

0 commit comments

Comments
 (0)