#![allow(clippy::arithmetic_side_effects)]
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(feature = "frozen-abi", feature(min_specialization))]
#[cfg(feature = "frozen-abi")]
extern crate std;
#[cfg(feature = "sysvar")]
pub mod sysvar;
#[cfg(feature = "frozen-abi")]
use solana_frozen_abi_macro::{AbiExample, StableAbi, StableAbiSample};
use solana_sdk_macro::CloneZeroed;
#[repr(C)]
#[cfg_attr(feature = "frozen-abi", derive(AbiExample, StableAbi, StableAbiSample))]
#[cfg_attr(
feature = "serde",
derive(serde_derive::Deserialize, serde_derive::Serialize)
)]
#[cfg_attr(feature = "wincode", derive(wincode::SchemaWrite, wincode::SchemaRead))]
#[derive(PartialEq, CloneZeroed, Debug)]
pub struct Rent {
pub lamports_per_byte: u64,
#[deprecated(since = "4.1.0", note = "Use `Rent::minimum_balance()` directly")]
pub exemption_threshold: [u8; 8],
#[deprecated(since = "4.1.0", note = "Rent no longer exists")]
pub burn_percent: u8,
}
pub const SIZE: usize = size_of::<u64>() + size_of::<[u8; 8]>() + size_of::<u8>(); const _: () = assert!(SIZE == 17);
const MAX_PERMITTED_DATA_LENGTH: u64 = 10 * 1024 * 1024;
pub const DEFAULT_LAMPORTS_PER_BYTE: u64 = 6_960;
const SIMD0194_EXEMPTION_THRESHOLD: [u8; 8] = [0, 0, 0, 0, 0, 0, 240, 63];
const CURRENT_EXEMPTION_THRESHOLD: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 64];
const SIMD0194_MAX_LAMPORTS_PER_BYTE: u64 = 1_759_197_129_867;
const CURRENT_MAX_LAMPORTS_PER_BYTE: u64 = 879_598_564_933;
const DEFAULT_BURN_PERCENT: u8 = 50;
pub const ACCOUNT_STORAGE_OVERHEAD: u64 = 128;
impl Default for Rent {
fn default() -> Self {
#[allow(deprecated)]
Self {
lamports_per_byte: DEFAULT_LAMPORTS_PER_BYTE,
exemption_threshold: SIMD0194_EXEMPTION_THRESHOLD,
burn_percent: DEFAULT_BURN_PERCENT,
}
}
}
impl Rent {
#[inline(always)]
pub fn minimum_balance(&self, data_len: usize) -> u64 {
self.try_minimum_balance(data_len)
.expect("Maximum permitted data length exceeded")
}
#[inline(always)]
pub fn minimum_balance_unchecked(&self, data_len: usize) -> u64 {
let bytes = data_len as u64;
#[allow(deprecated)]
if self.exemption_threshold == SIMD0194_EXEMPTION_THRESHOLD {
(ACCOUNT_STORAGE_OVERHEAD + bytes) * self.lamports_per_byte
} else if self.exemption_threshold == CURRENT_EXEMPTION_THRESHOLD {
2 * (ACCOUNT_STORAGE_OVERHEAD + bytes) * self.lamports_per_byte
} else {
#[cfg(not(target_arch = "bpf"))]
{
(((ACCOUNT_STORAGE_OVERHEAD + bytes) * self.lamports_per_byte) as f64
* f64::from_le_bytes(self.exemption_threshold)) as u64
}
#[cfg(target_arch = "bpf")]
panic!("Floating-point operations are not supported on BPF targets");
}
}
#[inline(always)]
pub fn try_minimum_balance(&self, data_len: usize) -> Option<u64> {
if data_len as u64 > MAX_PERMITTED_DATA_LENGTH {
return None;
}
#[allow(deprecated)]
if (self.lamports_per_byte > CURRENT_MAX_LAMPORTS_PER_BYTE
&& self.exemption_threshold == CURRENT_EXEMPTION_THRESHOLD)
|| (self.lamports_per_byte > SIMD0194_MAX_LAMPORTS_PER_BYTE
&& self.exemption_threshold == SIMD0194_EXEMPTION_THRESHOLD)
{
return None;
}
Some(self.minimum_balance_unchecked(data_len))
}
pub fn is_exempt(&self, balance: u64, data_len: usize) -> bool {
balance >= self.minimum_balance(data_len)
}
pub fn free() -> Self {
Self {
lamports_per_byte: 0,
..Rent::default()
}
}
pub fn with_lamports_per_byte(lamports_per_byte: u64) -> Self {
Self {
lamports_per_byte,
..Self::default()
}
}
}
#[cfg(test)]
mod tests {
use {super::*, proptest::proptest};
#[test]
fn test_size_of() {
assert_eq!(
wincode::serialized_size(&Rent::default()).unwrap() as usize,
SIZE,
);
}
#[test]
fn test_clone() {
#[allow(deprecated)]
let rent = Rent {
lamports_per_byte: 1,
exemption_threshold: 2.2f64.to_le_bytes(),
burn_percent: 3,
};
#[allow(clippy::clone_on_copy)]
let cloned_rent = rent.clone();
assert_eq!(cloned_rent, rent);
}
#[test]
fn test_exemption_threshold() {
assert_eq!(1f64.to_le_bytes(), SIMD0194_EXEMPTION_THRESHOLD);
assert_eq!(2f64.to_le_bytes(), CURRENT_EXEMPTION_THRESHOLD);
}
proptest! {
#[test]
fn test_minimum_balance(bytes in 0usize..=MAX_PERMITTED_DATA_LENGTH as usize) {
let default_rent = Rent::default();
#[allow(deprecated)]
let previous_rent = Rent {
lamports_per_byte: DEFAULT_LAMPORTS_PER_BYTE / 2,
exemption_threshold: 2.0f64.to_le_bytes(),
..Default::default()
};
let default_calc = default_rent.minimum_balance(bytes);
assert_eq!(default_calc, previous_rent.minimum_balance(bytes));
#[allow(deprecated)]
let float_calc = (((ACCOUNT_STORAGE_OVERHEAD + bytes as u64) * previous_rent.lamports_per_byte) as f64
* f64::from_le_bytes(previous_rent.exemption_threshold)) as u64;
assert_eq!(default_calc, float_calc);
}
}
}