1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! configuration for network rent

#[derive(Serialize, Deserialize, PartialEq, Clone, Copy, Debug)]
pub struct Rent {
    /// Rental rate
    pub lamports_per_byte_year: u64,

    /// exemption threshold, in years
    pub exemption_threshold: f64,

    // What portion of collected rent are to be destroyed, percentage-wise
    pub burn_percent: u8,
}

/// default rental rate in lamports/byte-year, based on:
///  2^^34 lamports per Sol
///  $1 per Sol
///  $0.01 per megabyte day
///  $3.65 per megabyte year
pub const DEFAULT_LAMPORTS_PER_BYTE_YEAR: u64 = 0; //17_179_869_184 / 100 * 365 / (1024 * 1024);

/// default amount of time (in years) the balance has to include rent for
pub const DEFAULT_EXEMPTION_THRESHOLD: f64 = 2.0;

/// default amount of rent to burn, as a fraction of std::u8::MAX
pub const DEFAULT_BURN_PERCENT: u8 = ((50usize * std::u8::MAX as usize) / 100usize) as u8;

impl Default for Rent {
    fn default() -> Self {
        Self {
            lamports_per_byte_year: DEFAULT_LAMPORTS_PER_BYTE_YEAR,
            exemption_threshold: DEFAULT_EXEMPTION_THRESHOLD,
            burn_percent: DEFAULT_BURN_PERCENT,
        }
    }
}

impl Rent {
    /// minimum balance due for a given size Account::data.len()
    pub fn minimum_balance(&self, data_len: usize) -> u64 {
        let bytes = data_len as u64;
        bytes * (self.exemption_threshold * self.lamports_per_byte_year as f64) as u64
    }

    /// whether a given balance and data_len would be exempt
    pub fn is_exempt(&self, balance: u64, data_len: usize) -> bool {
        balance >= self.minimum_balance(data_len)
    }

    /// rent due on account's data_len with balance
    pub fn due(&self, balance: u64, data_len: usize, years_elapsed: f64) -> (u64, bool) {
        if self.is_exempt(balance, data_len) {
            (0, true)
        } else {
            (
                ((self.lamports_per_byte_year * data_len as u64) as f64 * years_elapsed) as u64,
                false,
            )
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_due() {
        let rent = Rent::default();

        assert_eq!(
            rent.due(0, 1, 1.0),
            (
                DEFAULT_LAMPORTS_PER_BYTE_YEAR,
                DEFAULT_LAMPORTS_PER_BYTE_YEAR == 0
            )
        );
        assert_eq!(
            rent.due(
                DEFAULT_LAMPORTS_PER_BYTE_YEAR * DEFAULT_EXEMPTION_THRESHOLD as u64,
                1,
                1.0
            ),
            (0, true)
        );
    }

    // uncomment me and make my eprintlns macros
    //    #[test]
    //    fn test_rent_model() {
    //        use crate::timing::*;
    //
    //        const SECONDS_PER_YEAR: f64 = (365.25 * 24.0 * 60.0 * 60.0);
    //        const SLOTS_PER_YEAR: f64 =
    //            SECONDS_PER_YEAR / (DEFAULT_TICKS_PER_SLOT as f64 / DEFAULT_TICKS_PER_SECOND as f64);
    //
    //        let rent = Rent::default();
    //
    //        eprintln();
    //        // lamports charged per byte per slot at $1/MByear, rent per slot is zero
    //        eprintln(
    //            "{} lamports per byte-slot, rent.due(): {}",
    //            (1.0 / SLOTS_PER_YEAR) * DEFAULT_LAMPORTS_PER_BYTE_YEAR as f64,
    //            rent.due(0, 1, 1.0 / SLOTS_PER_YEAR).0,
    //        );
    //        // lamports charged per byte per _epoch_ starts to have some significant digits
    //        eprintln(
    //            "{} lamports per byte-epoch, rent.due(): {}",
    //            (1.0 / SLOTS_PER_YEAR)
    //                * (DEFAULT_LAMPORTS_PER_BYTE_YEAR * DEFAULT_SLOTS_PER_EPOCH) as f64,
    //            rent.due(
    //                0,
    //                1,
    //                (1.0 / SLOTS_PER_YEAR) * DEFAULT_SLOTS_PER_EPOCH as f64
    //            )
    //            .0,
    //        );
    //        // have a look at what a large-ish sysvar would cost, were it a real account...
    //        eprintln(
    //            "stake_history: {}kB == {} lamports per epoch",
    //            crate::sysvar::stake_history::StakeHistory::size_of() / 1024,
    //            rent.due(
    //                0,
    //                crate::sysvar::stake_history::StakeHistory::size_of(),
    //                (1.0 / SLOTS_PER_YEAR) * DEFAULT_SLOTS_PER_EPOCH as f64
    //            )
    //            .0,
    //        );
    //    }
}