diff --git a/genesis/src/main.rs b/genesis/src/main.rs index 1ed43402c0..42a4117fa7 100644 --- a/genesis/src/main.rs +++ b/genesis/src/main.rs @@ -11,7 +11,7 @@ use solana_sdk::genesis_block::Builder; use solana_sdk::hash::{hash, Hash}; use solana_sdk::poh_config::PohConfig; use solana_sdk::pubkey::Pubkey; -use solana_sdk::rent::Rent; +use solana_sdk::rent_calculator::RentCalculator; use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil}; use solana_sdk::system_program; use solana_sdk::timing; @@ -70,7 +70,11 @@ fn main() -> Result<(), Box> { let default_target_lamports_per_signature = &FeeCalculator::default() .target_lamports_per_signature .to_string(); - let default_lamports_per_byte_year = &Rent::default().lamports_per_byte_year.to_string(); + let default_lamports_per_byte_year = + &RentCalculator::default().lamports_per_byte_year.to_string(); + let default_rent_exemption_threshold = + &RentCalculator::default().exemption_threshold.to_string(); + let default_rent_burn_percentage = &RentCalculator::default().burn_percent.to_string(); let default_target_signatures_per_slot = &FeeCalculator::default() .target_signatures_per_slot .to_string(); @@ -184,6 +188,25 @@ fn main() -> Result<(), Box> { for accounts with data.", ), ) + .arg( + Arg::with_name("rent_exemption_threshold") + .long("rent-exemption-threshold") + .value_name("NUMBER") + .takes_value(true) + .default_value(default_rent_exemption_threshold) + .help( + "amount of time (in years) the balance has to include rent for \ + to qualify as rent exempted account.", + ), + ) + .arg( + Arg::with_name("rent_burn_percentage") + .long("rent-burn-percentage") + .value_name("NUMBER") + .takes_value(true) + .default_value(default_rent_burn_percentage) + .help("amount of rent to burn, as a fraction of std::u8::MAX."), + ) .arg( Arg::with_name("target_signatures_per_slot") .long("target-signatures-per-slot") @@ -315,6 +338,13 @@ fn main() -> Result<(), Box> { value_t_or_exit!(matches, "target_signatures_per_slot", usize); builder = builder.fee_calculator(FeeCalculator::new_derived(&fee_calculator, 0)); + let rent_calculator = RentCalculator { + lamports_per_byte_year: value_t_or_exit!(matches, "lamports_per_byte_year", u64), + exemption_threshold: value_t_or_exit!(matches, "rent_exemption_threshold", f64), + burn_percent: value_t_or_exit!(matches, "rent_burn_percentage", u8), + }; + builder = builder.rent_calculator(rent_calculator); + let mut poh_config = PohConfig::default(); poh_config.target_tick_duration = Duration::from_millis(value_t_or_exit!(matches, "target_tick_duration", u64)); diff --git a/programs/bpf/rust/sysval/src/lib.rs b/programs/bpf/rust/sysval/src/lib.rs index 9ff75f0880..d881a422e6 100644 --- a/programs/bpf/rust/sysval/src/lib.rs +++ b/programs/bpf/rust/sysval/src/lib.rs @@ -7,8 +7,9 @@ use solana_sdk::{ entrypoint, entrypoint::SUCCESS, pubkey::Pubkey, + rent_calculator, sysvar::{ - clock::Clock, fees::Fees, rewards::Rewards, slot_hashes::SlotHashes, + clock::Clock, fees::Fees, rent::Rent, rewards::Rewards, slot_hashes::SlotHashes, stake_history::StakeHistory, }, }; @@ -39,5 +40,16 @@ fn process_instruction(_program_id: &Pubkey, accounts: &mut [AccountInfo], _data let stake_history = StakeHistory::from_account_info(&accounts[6]).unwrap(); assert_eq!(stake_history.len(), 1); + let rent = Rent::from_account_info(&accounts[7]).unwrap(); + assert_eq!( + rent.rent_calculator.due( + rent_calculator::DEFAULT_LAMPORTS_PER_BYTE_YEAR + * rent_calculator::DEFAULT_EXEMPTION_THRESHOLD as u64, + 1, + 1.0 + ), + (0, true) + ); + SUCCESS } diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index fb420f14ec..b179bc73f6 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -83,7 +83,7 @@ mod bpf { use solana_sdk::instruction::{AccountMeta, Instruction}; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::{Keypair, KeypairUtil}; - use solana_sdk::sysvar::{clock, fees, rewards, slot_hashes, stake_history}; + use solana_sdk::sysvar::{clock, fees, rewards, slot_hashes, stake_history, rent}; use std::io::Read; use std::sync::Arc; @@ -132,6 +132,7 @@ mod bpf { AccountMeta::new(rewards::id(), false), AccountMeta::new(slot_hashes::id(), false), AccountMeta::new(stake_history::id(), false), + AccountMeta::new(rent::id(), false) ]; let instruction = Instruction::new(program_id, &1u8, account_metas); let result = bank_client.send_instruction(&mint_keypair, instruction); diff --git a/programs/vote_api/src/vote_instruction.rs b/programs/vote_api/src/vote_instruction.rs index 7ff9a2acc0..dccd433a8c 100644 --- a/programs/vote_api/src/vote_instruction.rs +++ b/programs/vote_api/src/vote_instruction.rs @@ -74,9 +74,9 @@ fn initialize_account(vote_pubkey: &Pubkey, node_pubkey: &Pubkey, commission: u8 } pub fn minimum_balance() -> u64 { - let rent = solana_sdk::rent::Rent::default(); + let rent_calculator = solana_sdk::rent_calculator::RentCalculator::default(); - rent.minimum_balance(VoteState::size_of()) + rent_calculator.minimum_balance(VoteState::size_of()) } pub fn create_account( @@ -279,8 +279,8 @@ mod tests { #[test] fn test_minimum_balance() { - let rent = solana_sdk::rent::Rent::default(); - let minimum_balance = rent.minimum_balance(VoteState::size_of()); + let rent_calculator = solana_sdk::rent_calculator::RentCalculator::default(); + let minimum_balance = rent_calculator.minimum_balance(VoteState::size_of()); // vote state cheaper than "my $0.02" ;) assert!(minimum_balance as f64 / 2f64.powf(34.0) < 0.02) } diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index afd088c60c..bab42554a6 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -41,7 +41,7 @@ use solana_sdk::{ signature::{Keypair, Signature}, system_transaction, sysvar::{ - clock, fees, rewards, + clock, fees, rent, rewards, slot_hashes::{self, SlotHashes}, stake_history, }, @@ -354,6 +354,7 @@ impl Bank { new.update_stake_history(Some(parent.epoch())); new.update_clock(); new.update_fees(); + new.update_rent(); new } @@ -426,6 +427,13 @@ impl Bank { self.store_account(&fees::id(), &fees::create_account(1, &self.fee_calculator)); } + fn update_rent(&self) { + self.store_account( + &rent::id(), + &rent::create_account(1, &self.rent_collector.rent_calculator), + ); + } + fn update_stake_history(&self, epoch: Option) { if epoch == Some(self.epoch()) { return; @@ -620,6 +628,14 @@ impl Bank { self.inflation = genesis_block.inflation; + let rent_calculator = genesis_block.rent_calculator; + self.rent_collector = RentCollector::new( + self.epoch, + &self.epoch_schedule, + self.slots_per_year, + &rent_calculator, + ); + // Add additional native programs specified in the genesis block for (name, program_id) in &genesis_block.native_instruction_processors { self.register_native_instruction_processor(name, program_id); diff --git a/runtime/src/rent_collector.rs b/runtime/src/rent_collector.rs index a57b0a37f6..ce8305d50f 100644 --- a/runtime/src/rent_collector.rs +++ b/runtime/src/rent_collector.rs @@ -1,13 +1,13 @@ //! calculate and collect rent from Accounts use crate::epoch_schedule::EpochSchedule; -use solana_sdk::{account::Account, clock::Epoch, rent::Rent}; +use solana_sdk::{account::Account, clock::Epoch, rent_calculator::RentCalculator}; #[derive(Default, Serialize, Deserialize, Clone)] pub struct RentCollector { pub epoch: Epoch, pub epoch_schedule: EpochSchedule, pub slots_per_year: f64, - pub rent: Rent, + pub rent_calculator: RentCalculator, } impl RentCollector { @@ -15,13 +15,13 @@ impl RentCollector { epoch: Epoch, epoch_schedule: &EpochSchedule, slots_per_year: f64, - rent: &Rent, + rent_calculator: &RentCalculator, ) -> Self { Self { epoch, epoch_schedule: *epoch_schedule, slots_per_year, - rent: *rent, + rent_calculator: *rent_calculator, } } @@ -42,7 +42,7 @@ impl RentCollector { .map(|epoch| self.epoch_schedule.get_slots_in_epoch(epoch + 1)) .sum(); - let (rent_due, exempt) = self.rent.due( + let (rent_due, exempt) = self.rent_calculator.due( account.lamports, account.data.len(), slots_elapsed as f64 / self.slots_per_year, diff --git a/sdk/src/genesis_block.rs b/sdk/src/genesis_block.rs index f51e7ccabc..1a0380d0ff 100644 --- a/sdk/src/genesis_block.rs +++ b/sdk/src/genesis_block.rs @@ -7,7 +7,7 @@ use crate::hash::{hash, Hash}; use crate::inflation::Inflation; use crate::poh_config::PohConfig; use crate::pubkey::Pubkey; -use crate::rent::Rent; +use crate::rent_calculator::RentCalculator; use crate::signature::{Keypair, KeypairUtil}; use crate::system_program::{self, solana_system_program}; use bincode::{deserialize, serialize}; @@ -28,8 +28,8 @@ pub struct GenesisBlock { pub slots_per_segment: u64, pub poh_config: PohConfig, pub fee_calculator: FeeCalculator, + pub rent_calculator: RentCalculator, pub inflation: Inflation, - pub rent: Rent, } // useful for basic tests @@ -61,7 +61,7 @@ impl Default for GenesisBlock { poh_config: PohConfig::default(), inflation: Inflation::default(), fee_calculator: FeeCalculator::default(), - rent: Rent::default(), + rent_calculator: RentCalculator::default(), } } } @@ -147,6 +147,10 @@ impl Builder { self.genesis_block.inflation = inflation; self } + pub fn rent_calculator(mut self, rent_calculator: RentCalculator) -> Self { + self.genesis_block.rent_calculator = rent_calculator; + self + } } impl GenesisBlock { diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index dfa03c4815..63c133edf1 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -13,7 +13,7 @@ pub mod native_loader; pub mod packet; pub mod poh_config; pub mod pubkey; -pub mod rent; +pub mod rent_calculator; pub mod rpc_port; pub mod short_vec; pub mod system_instruction; diff --git a/sdk/src/rent.rs b/sdk/src/rent_calculator.rs similarity index 87% rename from sdk/src/rent.rs rename to sdk/src/rent_calculator.rs index 5a388a7bc8..6f1842121b 100644 --- a/sdk/src/rent.rs +++ b/sdk/src/rent_calculator.rs @@ -1,7 +1,7 @@ //! configuration for network rent #[derive(Serialize, Deserialize, PartialEq, Clone, Copy, Debug)] -pub struct Rent { +pub struct RentCalculator { /// Rental rate pub lamports_per_byte_year: u64, @@ -25,7 +25,7 @@ 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 { +impl Default for RentCalculator { fn default() -> Self { Self { lamports_per_byte_year: DEFAULT_LAMPORTS_PER_BYTE_YEAR, @@ -35,7 +35,7 @@ impl Default for Rent { } } -impl Rent { +impl RentCalculator { /// 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; @@ -66,17 +66,17 @@ mod tests { #[test] fn test_due() { - let rent = Rent::default(); + let rent_calculator = RentCalculator::default(); assert_eq!( - rent.due(0, 1, 1.0), + rent_calculator.due(0, 1, 1.0), ( DEFAULT_LAMPORTS_PER_BYTE_YEAR, DEFAULT_LAMPORTS_PER_BYTE_YEAR == 0 ) ); assert_eq!( - rent.due( + rent_calculator.due( DEFAULT_LAMPORTS_PER_BYTE_YEAR * DEFAULT_EXEMPTION_THRESHOLD as u64, 1, 1.0 @@ -94,21 +94,21 @@ mod tests { // const SLOTS_PER_YEAR: f64 = // SECONDS_PER_YEAR / (DEFAULT_TICKS_PER_SLOT as f64 / DEFAULT_TICKS_PER_SECOND as f64); // - // let rent = Rent::default(); + // let rent_calculator = RentCalculator::default(); // // eprintln(); // // lamports charged per byte per slot at $1/MByear, rent per slot is zero // eprintln( - // "{} lamports per byte-slot, rent.due(): {}", + // "{} lamports per byte-slot, rent_calculator.due(): {}", // (1.0 / SLOTS_PER_YEAR) * DEFAULT_LAMPORTS_PER_BYTE_YEAR as f64, - // rent.due(0, 1, 1.0 / SLOTS_PER_YEAR).0, + // rent_calculator.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(): {}", + // "{} lamports per byte-epoch, rent_calculator.due(): {}", // (1.0 / SLOTS_PER_YEAR) // * (DEFAULT_LAMPORTS_PER_BYTE_YEAR * DEFAULT_SLOTS_PER_EPOCH) as f64, - // rent.due( + // rent_calculator.due( // 0, // 1, // (1.0 / SLOTS_PER_YEAR) * DEFAULT_SLOTS_PER_EPOCH as f64 @@ -119,7 +119,7 @@ mod tests { // eprintln( // "stake_history: {}kB == {} lamports per epoch", // crate::sysvar::stake_history::StakeHistory::size_of() / 1024, - // rent.due( + // rent_calculator.due( // 0, // crate::sysvar::stake_history::StakeHistory::size_of(), // (1.0 / SLOTS_PER_YEAR) * DEFAULT_SLOTS_PER_EPOCH as f64 diff --git a/sdk/src/sysvar/mod.rs b/sdk/src/sysvar/mod.rs index 4a955e1161..c5294396e9 100644 --- a/sdk/src/sysvar/mod.rs +++ b/sdk/src/sysvar/mod.rs @@ -4,6 +4,7 @@ use crate::pubkey::Pubkey; pub mod clock; pub mod fees; +pub mod rent; pub mod rewards; pub mod slot_hashes; pub mod stake_history; diff --git a/sdk/src/sysvar/rent.rs b/sdk/src/sysvar/rent.rs new file mode 100644 index 0000000000..b8d72b7b13 --- /dev/null +++ b/sdk/src/sysvar/rent.rs @@ -0,0 +1,63 @@ +//! This account contains the current cluster rent +//! +use crate::account::Account; +use crate::account_info::AccountInfo; +use crate::rent_calculator::RentCalculator; +use crate::sysvar; +use bincode::serialized_size; + +/// rent account pubkey +const ID: [u8; 32] = [ + 6, 167, 213, 23, 25, 44, 92, 81, 33, 140, 201, 76, 61, 74, 241, 127, 88, 218, 238, 8, 155, 161, + 253, 68, 227, 219, 217, 138, 0, 0, 0, 0, +]; + +crate::solana_name_id!(ID, "SysvarRent111111111111111111111111111111111"); + +#[repr(C)] +#[derive(Serialize, Deserialize, Debug, Default)] +pub struct Rent { + pub rent_calculator: RentCalculator, +} + +impl Rent { + pub fn from_account(account: &Account) -> Option { + account.deserialize_data().ok() + } + pub fn to_account(&self, account: &mut Account) -> Option<()> { + account.serialize_data(self).ok() + } + pub fn from_account_info(account: &AccountInfo) -> Option { + account.deserialize_data().ok() + } + pub fn to_account_info(&self, account: &mut AccountInfo) -> Option<()> { + account.serialize_data(self).ok() + } + pub fn size_of() -> usize { + serialized_size(&Rent::default()).unwrap() as usize + } +} + +pub fn create_account(lamports: u64, rent_calculator: &RentCalculator) -> Account { + Account::new_data( + lamports, + &Rent { + rent_calculator: *rent_calculator, + }, + &sysvar::id(), + ) + .unwrap() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_fees_create_account() { + let lamports = 42; + let account = create_account(lamports, &RentCalculator::default()); + let rent = Rent::from_account(&account).unwrap(); + assert_eq!(rent.rent_calculator, RentCalculator::default()); + } +}