epoch_rewards_hasher.rs 3.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. use {
  2. crate::bank::partitioned_epoch_rewards::PartitionedStakeRewards,
  3. solana_epoch_rewards_hasher::EpochRewardsHasher, solana_hash::Hash,
  4. };
  5. pub(in crate::bank::partitioned_epoch_rewards) fn hash_rewards_into_partitions(
  6. stake_rewards: &PartitionedStakeRewards,
  7. parent_blockhash: &Hash,
  8. num_partitions: usize,
  9. ) -> Vec<Vec<usize>> {
  10. let hasher = EpochRewardsHasher::new(num_partitions, parent_blockhash);
  11. let mut indices = vec![vec![]; num_partitions];
  12. for (i, reward) in stake_rewards.enumerated_rewards_iter() {
  13. // clone here so the hasher's state is reused on each call to `hash_address_to_partition`.
  14. // This prevents us from re-hashing the seed each time.
  15. // The clone is explicit (as opposed to an implicit copy) so it is clear this is intended.
  16. let partition_index = hasher
  17. .clone()
  18. .hash_address_to_partition(&reward.stake_pubkey);
  19. indices[partition_index].push(i);
  20. }
  21. indices
  22. }
  23. #[cfg(test)]
  24. mod tests {
  25. use {
  26. super::*,
  27. crate::bank::{
  28. partitioned_epoch_rewards::{PartitionedStakeReward, REWARD_CALCULATION_NUM_BLOCKS},
  29. tests::create_genesis_config,
  30. Bank,
  31. },
  32. solana_epoch_schedule::EpochSchedule,
  33. solana_native_token::LAMPORTS_PER_SOL,
  34. std::sync::Arc,
  35. };
  36. #[test]
  37. fn test_hash_rewards_into_partitions() {
  38. // setup the expected number of stake rewards
  39. let expected_num = 12345;
  40. let stake_rewards = (0..expected_num)
  41. .map(|_| Some(PartitionedStakeReward::new_random()))
  42. .collect::<PartitionedStakeRewards>();
  43. let partition_indices = hash_rewards_into_partitions(&stake_rewards, &Hash::default(), 5);
  44. let total_num_after_hash_partition: usize = partition_indices.iter().map(|x| x.len()).sum();
  45. // assert total is same, so nothing is dropped or duplicated
  46. assert_eq!(expected_num, total_num_after_hash_partition);
  47. }
  48. #[test]
  49. fn test_hash_rewards_into_partitions_empty() {
  50. let stake_rewards = PartitionedStakeRewards::default();
  51. let num_partitions = 5;
  52. let partition_indices =
  53. hash_rewards_into_partitions(&stake_rewards, &Hash::default(), num_partitions);
  54. assert_eq!(partition_indices.len(), num_partitions);
  55. for indices in partition_indices.iter().take(num_partitions) {
  56. assert!(indices.is_empty());
  57. }
  58. }
  59. /// Test that reward partition range panics when passing out of range partition index
  60. #[test]
  61. #[should_panic(expected = "index out of bounds: the len is 10 but the index is 15")]
  62. fn test_get_stake_rewards_partition_range_panic() {
  63. let (mut genesis_config, _mint_keypair) =
  64. create_genesis_config(1_000_000 * LAMPORTS_PER_SOL);
  65. genesis_config.epoch_schedule = EpochSchedule::custom(432000, 432000, false);
  66. let mut bank = Bank::new_for_tests(&genesis_config);
  67. // simulate 40K - 1 rewards, the expected num of credit blocks should be 10.
  68. let expected_num = 40959;
  69. let stake_rewards = (0..expected_num)
  70. .map(|_| Some(PartitionedStakeReward::new_random()))
  71. .collect::<PartitionedStakeRewards>();
  72. let partition_indices =
  73. hash_rewards_into_partitions(&stake_rewards, &Hash::new_from_array([1; 32]), 10);
  74. bank.set_epoch_reward_status_distribution(
  75. bank.block_height() + REWARD_CALCULATION_NUM_BLOCKS,
  76. Arc::new(stake_rewards),
  77. partition_indices.clone(),
  78. );
  79. // This call should panic, i.e. 15 is out of the num_credit_blocks
  80. let _range = &partition_indices[15];
  81. }
  82. }