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