solana_program/
epoch_rewards.rs

1//! A type to hold data for the [`EpochRewards` sysvar][sv].
2//!
3//! [sv]: https://docs.solanalabs.com/runtime/sysvars#epochrewards
4//!
5//! The sysvar ID is declared in [`sysvar::epoch_rewards`].
6//!
7//! [`sysvar::epoch_rewards`]: crate::sysvar::epoch_rewards
8
9use {crate::hash::Hash, solana_sdk_macro::CloneZeroed};
10
11#[repr(C, align(16))]
12#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
13#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Default, CloneZeroed)]
14pub struct EpochRewards {
15    /// The starting block height of the rewards distribution in the current
16    /// epoch
17    pub distribution_starting_block_height: u64,
18
19    /// Number of partitions in the rewards distribution in the current epoch,
20    /// used to generate an EpochRewardsHasher
21    pub num_partitions: u64,
22
23    /// The blockhash of the parent block of the first block in the epoch, used
24    /// to seed an EpochRewardsHasher
25    pub parent_blockhash: Hash,
26
27    /// The total rewards points calculated for the current epoch, where points
28    /// equals the sum of (delegated stake * credits observed) for all
29    /// delegations
30    pub total_points: u128,
31
32    /// The total rewards calculated for the current epoch. This may be greater
33    /// than the total `distributed_rewards` at the end of the rewards period,
34    /// due to rounding and inability to deliver rewards smaller than 1 lamport.
35    pub total_rewards: u64,
36
37    /// The rewards currently distributed for the current epoch, in lamports
38    pub distributed_rewards: u64,
39
40    /// Whether the rewards period (including calculation and distribution) is
41    /// active
42    pub active: bool,
43}
44
45impl EpochRewards {
46    pub fn distribute(&mut self, amount: u64) {
47        let new_distributed_rewards = self.distributed_rewards.saturating_add(amount);
48        assert!(new_distributed_rewards <= self.total_rewards);
49        self.distributed_rewards = new_distributed_rewards;
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    impl EpochRewards {
58        pub fn new(
59            total_rewards: u64,
60            distributed_rewards: u64,
61            distribution_starting_block_height: u64,
62        ) -> Self {
63            Self {
64                total_rewards,
65                distributed_rewards,
66                distribution_starting_block_height,
67                ..Self::default()
68            }
69        }
70    }
71
72    #[test]
73    fn test_epoch_rewards_new() {
74        let epoch_rewards = EpochRewards::new(100, 0, 64);
75
76        assert_eq!(epoch_rewards.total_rewards, 100);
77        assert_eq!(epoch_rewards.distributed_rewards, 0);
78        assert_eq!(epoch_rewards.distribution_starting_block_height, 64);
79    }
80
81    #[test]
82    fn test_epoch_rewards_distribute() {
83        let mut epoch_rewards = EpochRewards::new(100, 0, 64);
84        epoch_rewards.distribute(100);
85
86        assert_eq!(epoch_rewards.total_rewards, 100);
87        assert_eq!(epoch_rewards.distributed_rewards, 100);
88    }
89
90    #[test]
91    #[should_panic(expected = "new_distributed_rewards <= self.total_rewards")]
92    fn test_epoch_rewards_distribute_panic() {
93        let mut epoch_rewards = EpochRewards::new(100, 0, 64);
94        epoch_rewards.distribute(200);
95    }
96}