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