plumb some rent (#5610)

* plumb some rent

* nits

* fixups

* fixups

* fixups
This commit is contained in:
Rob Walker
2019-08-23 14:04:53 -07:00
committed by GitHub
parent 9b8d59d2e9
commit 0ffe7a9c8f
19 changed files with 385 additions and 143 deletions

View File

@ -871,7 +871,8 @@ pub mod tests {
"owner": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], "owner": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
"lamports": 20, "lamports": 20,
"data": [], "data": [],
"executable": false "executable": false,
"rent_epoch": 0
}, },
"id":1} "id":1}
"#; "#;
@ -903,7 +904,8 @@ pub mod tests {
"owner": {:?}, "owner": {:?},
"lamports": 20, "lamports": 20,
"data": [], "data": [],
"executable": false "executable": false,
"rent_epoch": 0
}}]], }}]],
"id":1}} "id":1}}
"#, "#,

View File

@ -429,7 +429,8 @@ mod tests {
"owner": budget_program_id, "owner": budget_program_id,
"lamports": 51, "lamports": 51,
"data": expected_data, "data": expected_data,
"executable": executable, "executable": executable,
"rent_epoch": 0,
}, },
"subscription": 0, "subscription": 0,
} }
@ -574,6 +575,7 @@ mod tests {
"lamports": 100, "lamports": 100,
"data": [], "data": [],
"executable": false, "executable": false,
"rent_epoch": 0,
}, },
"subscription": 0, "subscription": 0,
} }

View File

@ -348,7 +348,7 @@ mod tests {
subscriptions.check_account(&alice.pubkey(), 0, &bank_forks); subscriptions.check_account(&alice.pubkey(), 0, &bank_forks);
let string = transport_receiver.poll(); let string = transport_receiver.poll();
if let Async::Ready(Some(response)) = string.unwrap() { if let Async::Ready(Some(response)) = string.unwrap() {
let expected = format!(r#"{{"jsonrpc":"2.0","method":"accountNotification","params":{{"result":{{"data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"executable":false,"lamports":1,"owner":[2,203,81,223,225,24,34,35,203,214,138,130,144,208,35,77,63,16,87,51,47,198,115,123,98,188,19,160,0,0,0,0]}},"subscription":0}}}}"#); let expected = format!(r#"{{"jsonrpc":"2.0","method":"accountNotification","params":{{"result":{{"data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"executable":false,"lamports":1,"owner":[2,203,81,223,225,24,34,35,203,214,138,130,144,208,35,77,63,16,87,51,47,198,115,123,98,188,19,160,0,0,0,0],"rent_epoch":0}},"subscription":0}}}}"#);
assert_eq!(expected, response); assert_eq!(expected, response);
} }
@ -403,7 +403,7 @@ mod tests {
subscriptions.check_program(&solana_budget_api::id(), 0, &bank_forks); subscriptions.check_program(&solana_budget_api::id(), 0, &bank_forks);
let string = transport_receiver.poll(); let string = transport_receiver.poll();
if let Async::Ready(Some(response)) = string.unwrap() { if let Async::Ready(Some(response)) = string.unwrap() {
let expected = format!(r#"{{"jsonrpc":"2.0","method":"programNotification","params":{{"result":["{:?}",{{"data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"executable":false,"lamports":1,"owner":[2,203,81,223,225,24,34,35,203,214,138,130,144,208,35,77,63,16,87,51,47,198,115,123,98,188,19,160,0,0,0,0]}}],"subscription":0}}}}"#, alice.pubkey()); let expected = format!(r#"{{"jsonrpc":"2.0","method":"programNotification","params":{{"result":["{:?}",{{"data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"executable":false,"lamports":1,"owner":[2,203,81,223,225,24,34,35,203,214,138,130,144,208,35,77,63,16,87,51,47,198,115,123,98,188,19,160,0,0,0,0],"rent_epoch":0}}],"subscription":0}}}}"#, alice.pubkey());
assert_eq!(expected, response); assert_eq!(expected, response);
} }

View File

@ -8,6 +8,7 @@ use solana_sdk::genesis_block::Builder;
use solana_sdk::hash::{hash, Hash}; use solana_sdk::hash::{hash, Hash};
use solana_sdk::poh_config::PohConfig; use solana_sdk::poh_config::PohConfig;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use solana_sdk::rent::Rent;
use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil}; use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil};
use solana_sdk::system_program; use solana_sdk::system_program;
use solana_sdk::timing; use solana_sdk::timing;
@ -59,6 +60,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
let default_target_lamports_per_signature = &FeeCalculator::default() let default_target_lamports_per_signature = &FeeCalculator::default()
.target_lamports_per_signature .target_lamports_per_signature
.to_string(); .to_string();
let default_lamports_per_byte_year = &Rent::default().lamports_per_byte_year.to_string();
let default_target_signatures_per_slot = &FeeCalculator::default() let default_target_signatures_per_slot = &FeeCalculator::default()
.target_signatures_per_slot .target_signatures_per_slot
.to_string(); .to_string();
@ -161,6 +163,17 @@ fn main() -> Result<(), Box<dyn error::Error>> {
verification when the cluster is operating at target-signatures-per-slot", verification when the cluster is operating at target-signatures-per-slot",
), ),
) )
.arg(
Arg::with_name("lamports_per_byte_year")
.long("lamports-per-byte-year")
.value_name("LAMPORTS")
.takes_value(true)
.default_value(default_lamports_per_byte_year)
.help(
"The cost in lamports that the cluster will charge per byte per year \
for accounts with data.",
),
)
.arg( .arg(
Arg::with_name("target_signatures_per_slot") Arg::with_name("target_signatures_per_slot")
.long("target-signatures-per-slot") .long("target-signatures-per-slot")

View File

@ -760,7 +760,7 @@ mod tests {
lamports: 1, lamports: 1,
data: bincode::serialize(&LibraAccountState::create_unallocated()).unwrap(), data: bincode::serialize(&LibraAccountState::create_unallocated()).unwrap(),
owner: id(), owner: id(),
executable: false, ..Account::default()
}; };
Self::new(key, account) Self::new(key, account)
} }
@ -768,9 +768,8 @@ mod tests {
pub fn create_genesis(amount: u64) -> Self { pub fn create_genesis(amount: u64) -> Self {
let account = Account { let account = Account {
lamports: 1, lamports: 1,
data: vec![],
owner: id(), owner: id(),
executable: false, ..Account::default()
}; };
let mut genesis = Self::new(Pubkey::default(), account); let mut genesis = Self::new(Pubkey::default(), account);
genesis.account.data = genesis.account.data =

View File

@ -547,10 +547,8 @@ mod tests {
let mut account = StorageAccount { let mut account = StorageAccount {
id: Pubkey::default(), id: Pubkey::default(),
account: &mut Account { account: &mut Account {
lamports: 0,
data: vec![],
owner: id(), owner: id(),
executable: false, ..Account::default()
}, },
}; };
let segment_index = 0; let segment_index = 0;

View File

@ -1,11 +1,9 @@
use crate::accounts_db::{ use crate::accounts_db::{AccountInfo, AccountStorage, AccountsDB, AppendVecId, ErrorCounters};
AccountInfo, AccountStorage, AccountsDB, AppendVecId, ErrorCounters, InstructionAccounts,
InstructionCredits, InstructionLoaders,
};
use crate::accounts_index::{AccountsIndex, Fork}; use crate::accounts_index::{AccountsIndex, Fork};
use crate::append_vec::StoredAccount; use crate::append_vec::StoredAccount;
use crate::blockhash_queue::BlockhashQueue; use crate::blockhash_queue::BlockhashQueue;
use crate::message_processor::has_duplicates; use crate::message_processor::has_duplicates;
use crate::rent_collector::RentCollector;
use bincode::serialize; use bincode::serialize;
use log::*; use log::*;
use rayon::slice::ParallelSliceMut; use rayon::slice::ParallelSliceMut;
@ -46,6 +44,12 @@ pub struct Accounts {
credit_only_account_locks: Arc<RwLock<Option<HashMap<Pubkey, CreditOnlyLock>>>>, credit_only_account_locks: Arc<RwLock<Option<HashMap<Pubkey, CreditOnlyLock>>>>,
} }
// for the load instructions
pub type TransactionAccounts = Vec<Account>;
pub type TransactionCredits = Vec<u64>;
pub type TransactionRents = Vec<u64>;
pub type TransactionLoaders = Vec<Vec<(Pubkey, Account)>>;
impl Accounts { impl Accounts {
pub fn new(paths: Option<String>) -> Self { pub fn new(paths: Option<String>) -> Self {
let accounts_db = Arc::new(AccountsDB::new(paths)); let accounts_db = Arc::new(AccountsDB::new(paths));
@ -82,7 +86,8 @@ impl Accounts {
tx: &Transaction, tx: &Transaction,
fee: u64, fee: u64,
error_counters: &mut ErrorCounters, error_counters: &mut ErrorCounters,
) -> Result<(Vec<Account>, InstructionCredits)> { rent_collector: &RentCollector,
) -> Result<(TransactionAccounts, TransactionCredits, TransactionRents)> {
// Copy all the accounts // Copy all the accounts
let message = tx.message(); let message = tx.message();
if tx.signatures.is_empty() && fee != 0 { if tx.signatures.is_empty() && fee != 0 {
@ -96,30 +101,35 @@ impl Accounts {
// There is no way to predict what program will execute without an error // There is no way to predict what program will execute without an error
// If a fee can pay for execution then the program will be scheduled // If a fee can pay for execution then the program will be scheduled
let mut called_accounts: Vec<Account> = vec![]; let mut accounts: TransactionAccounts = vec![];
let mut credits: InstructionCredits = vec![]; let mut credits: TransactionCredits = vec![];
for key in message.account_keys.iter() { let mut rents: TransactionRents = vec![];
if !message.program_ids().contains(&key) { for key in message
called_accounts.push( .account_keys
AccountsDB::load(storage, ancestors, accounts_index, key) .iter()
.map(|(account, _)| account) .filter(|key| !message.program_ids().contains(&key))
.unwrap_or_default(), {
); let (account, rent) = AccountsDB::load(storage, ancestors, accounts_index, key)
credits.push(0); .and_then(|(account, _)| rent_collector.update(account))
} .unwrap_or_default();
accounts.push(account);
credits.push(0);
rents.push(rent);
} }
if called_accounts.is_empty() || called_accounts[0].lamports == 0 {
if accounts.is_empty() || accounts[0].lamports == 0 {
error_counters.account_not_found += 1; error_counters.account_not_found += 1;
Err(TransactionError::AccountNotFound) Err(TransactionError::AccountNotFound)
} else if called_accounts[0].owner != system_program::id() { } else if accounts[0].owner != system_program::id() {
error_counters.invalid_account_for_fee += 1; error_counters.invalid_account_for_fee += 1;
Err(TransactionError::InvalidAccountForFee) Err(TransactionError::InvalidAccountForFee)
} else if called_accounts[0].lamports < fee { } else if accounts[0].lamports < fee {
error_counters.insufficient_funds += 1; error_counters.insufficient_funds += 1;
Err(TransactionError::InsufficientFundsForFee) Err(TransactionError::InsufficientFundsForFee)
} else { } else {
called_accounts[0].lamports -= fee; accounts[0].lamports -= fee;
Ok((called_accounts, credits)) Ok((accounts, credits, rents))
} }
} }
} }
@ -174,7 +184,7 @@ impl Accounts {
accounts_index: &AccountsIndex<AccountInfo>, accounts_index: &AccountsIndex<AccountInfo>,
tx: &Transaction, tx: &Transaction,
error_counters: &mut ErrorCounters, error_counters: &mut ErrorCounters,
) -> Result<Vec<Vec<(Pubkey, Account)>>> { ) -> Result<TransactionLoaders> {
let message = tx.message(); let message = tx.message();
message message
.instructions .instructions
@ -203,7 +213,15 @@ impl Accounts {
lock_results: Vec<Result<()>>, lock_results: Vec<Result<()>>,
hash_queue: &BlockhashQueue, hash_queue: &BlockhashQueue,
error_counters: &mut ErrorCounters, error_counters: &mut ErrorCounters,
) -> Vec<Result<(InstructionAccounts, InstructionLoaders, InstructionCredits)>> { rent_collector: &RentCollector,
) -> Vec<
Result<(
TransactionAccounts,
TransactionLoaders,
TransactionCredits,
TransactionRents,
)>,
> {
//PERF: hold the lock to scan for the references, but not to clone the accounts //PERF: hold the lock to scan for the references, but not to clone the accounts
//TODO: two locks usually leads to deadlocks, should this be one structure? //TODO: two locks usually leads to deadlocks, should this be one structure?
let accounts_index = self.accounts_db.accounts_index.read().unwrap(); let accounts_index = self.accounts_db.accounts_index.read().unwrap();
@ -217,13 +235,14 @@ impl Accounts {
.ok_or(TransactionError::BlockhashNotFound)?; .ok_or(TransactionError::BlockhashNotFound)?;
let fee = fee_calculator.calculate_fee(tx.message()); let fee = fee_calculator.calculate_fee(tx.message());
let (accounts, credits) = Self::load_tx_accounts( let (accounts, credits, rents) = Self::load_tx_accounts(
&storage, &storage,
ancestors, ancestors,
&accounts_index, &accounts_index,
tx, tx,
fee, fee,
error_counters, error_counters,
rent_collector,
)?; )?;
let loaders = Self::load_loaders( let loaders = Self::load_loaders(
&storage, &storage,
@ -232,7 +251,7 @@ impl Accounts {
tx, tx,
error_counters, error_counters,
)?; )?;
Ok((accounts, loaders, credits)) Ok((accounts, loaders, credits, rents))
} }
(_, Err(e)) => Err(e), (_, Err(e)) => Err(e),
}) })
@ -503,7 +522,12 @@ impl Accounts {
fork: Fork, fork: Fork,
txs: &[Transaction], txs: &[Transaction],
res: &[Result<()>], res: &[Result<()>],
loaded: &mut [Result<(InstructionAccounts, InstructionLoaders, InstructionCredits)>], loaded: &mut [Result<(
TransactionAccounts,
TransactionLoaders,
TransactionCredits,
TransactionRents,
)>],
) { ) {
let accounts_to_store = self.collect_accounts_to_store(txs, res, loaded); let accounts_to_store = self.collect_accounts_to_store(txs, res, loaded);
self.accounts_db.store(fork, &accounts_to_store); self.accounts_db.store(fork, &accounts_to_store);
@ -578,7 +602,12 @@ impl Accounts {
&self, &self,
txs: &'a [Transaction], txs: &'a [Transaction],
res: &'a [Result<()>], res: &'a [Result<()>],
loaded: &'a mut [Result<(InstructionAccounts, InstructionLoaders, InstructionCredits)>], loaded: &'a mut [Result<(
TransactionAccounts,
TransactionLoaders,
TransactionCredits,
TransactionRents,
)>],
) -> Vec<(&'a Pubkey, &'a Account)> { ) -> Vec<(&'a Pubkey, &'a Account)> {
let mut accounts = Vec::new(); let mut accounts = Vec::new();
for (i, raccs) in loaded.iter_mut().enumerate() { for (i, raccs) in loaded.iter_mut().enumerate() {
@ -651,7 +680,14 @@ mod tests {
ka: &Vec<(Pubkey, Account)>, ka: &Vec<(Pubkey, Account)>,
fee_calculator: &FeeCalculator, fee_calculator: &FeeCalculator,
error_counters: &mut ErrorCounters, error_counters: &mut ErrorCounters,
) -> Vec<Result<(InstructionAccounts, InstructionLoaders, InstructionCredits)>> { ) -> Vec<
Result<(
TransactionAccounts,
TransactionLoaders,
TransactionCredits,
TransactionRents,
)>,
> {
let mut hash_queue = BlockhashQueue::new(100); let mut hash_queue = BlockhashQueue::new(100);
hash_queue.register_hash(&tx.message().recent_blockhash, &fee_calculator); hash_queue.register_hash(&tx.message().recent_blockhash, &fee_calculator);
let accounts = Accounts::new(None); let accounts = Accounts::new(None);
@ -660,8 +696,15 @@ mod tests {
} }
let ancestors = vec![(0, 0)].into_iter().collect(); let ancestors = vec![(0, 0)].into_iter().collect();
let res = let rent_collector = RentCollector::default();
accounts.load_accounts(&ancestors, &[tx], vec![Ok(())], &hash_queue, error_counters); let res = accounts.load_accounts(
&ancestors,
&[tx],
vec![Ok(())],
&hash_queue,
error_counters,
&rent_collector,
);
res res
} }
@ -669,7 +712,14 @@ mod tests {
tx: Transaction, tx: Transaction,
ka: &Vec<(Pubkey, Account)>, ka: &Vec<(Pubkey, Account)>,
error_counters: &mut ErrorCounters, error_counters: &mut ErrorCounters,
) -> Vec<Result<(InstructionAccounts, InstructionLoaders, InstructionCredits)>> { ) -> Vec<
Result<(
TransactionAccounts,
TransactionLoaders,
TransactionCredits,
TransactionRents,
)>,
> {
let fee_calculator = FeeCalculator::default(); let fee_calculator = FeeCalculator::default();
load_accounts_with_fee(tx, ka, &fee_calculator, error_counters) load_accounts_with_fee(tx, ka, &fee_calculator, error_counters)
} }
@ -825,10 +875,12 @@ mod tests {
let key0 = keypair.pubkey(); let key0 = keypair.pubkey();
let key1 = Pubkey::new(&[5u8; 32]); let key1 = Pubkey::new(&[5u8; 32]);
let account = Account::new(1, 1, &Pubkey::default()); let mut account = Account::new(1, 1, &Pubkey::default());
account.rent_epoch = 1;
accounts.push((key0, account)); accounts.push((key0, account));
let account = Account::new(2, 1, &Pubkey::default()); let mut account = Account::new(2, 1, &Pubkey::default());
account.rent_epoch = 1;
accounts.push((key1, account)); accounts.push((key1, account));
let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])]; let instructions = vec![CompiledInstruction::new(2, &(), vec![0, 1])];
@ -845,13 +897,18 @@ mod tests {
assert_eq!(error_counters.account_not_found, 0); assert_eq!(error_counters.account_not_found, 0);
assert_eq!(loaded_accounts.len(), 1); assert_eq!(loaded_accounts.len(), 1);
match &loaded_accounts[0] { match &loaded_accounts[0] {
Ok((instruction_accounts, instruction_loaders, instruction_credits)) => { Ok((
assert_eq!(instruction_accounts.len(), 2); transaction_accounts,
assert_eq!(instruction_accounts[0], accounts[0].1); transaction_loaders,
assert_eq!(instruction_loaders.len(), 1); transaction_credits,
assert_eq!(instruction_loaders[0].len(), 0); _transaction_rents,
assert_eq!(instruction_credits.len(), 2); )) => {
assert_eq!(instruction_credits, &vec![0, 0]); assert_eq!(transaction_accounts.len(), 2);
assert_eq!(transaction_accounts[0], accounts[0].1);
assert_eq!(transaction_loaders.len(), 1);
assert_eq!(transaction_loaders[0].len(), 0);
assert_eq!(transaction_credits.len(), 2);
assert_eq!(transaction_credits, &vec![0, 0]);
} }
Err(e) => Err(e).unwrap(), Err(e) => Err(e).unwrap(),
} }
@ -996,21 +1053,25 @@ mod tests {
let key2 = Pubkey::new(&[6u8; 32]); let key2 = Pubkey::new(&[6u8; 32]);
let key3 = Pubkey::new(&[7u8; 32]); let key3 = Pubkey::new(&[7u8; 32]);
let account = Account::new(1, 1, &Pubkey::default()); let mut account = Account::new(1, 1, &Pubkey::default());
account.rent_epoch = 1;
accounts.push((key0, account)); accounts.push((key0, account));
let mut account = Account::new(40, 1, &Pubkey::default()); let mut account = Account::new(40, 1, &Pubkey::default());
account.executable = true; account.executable = true;
account.rent_epoch = 1;
account.owner = native_loader::id(); account.owner = native_loader::id();
accounts.push((key1, account)); accounts.push((key1, account));
let mut account = Account::new(41, 1, &Pubkey::default()); let mut account = Account::new(41, 1, &Pubkey::default());
account.executable = true; account.executable = true;
account.rent_epoch = 1;
account.owner = key1; account.owner = key1;
accounts.push((key2, account)); accounts.push((key2, account));
let mut account = Account::new(42, 1, &Pubkey::default()); let mut account = Account::new(42, 1, &Pubkey::default());
account.executable = true; account.executable = true;
account.rent_epoch = 1;
account.owner = key2; account.owner = key2;
accounts.push((key3, account)); accounts.push((key3, account));
@ -1031,15 +1092,20 @@ mod tests {
assert_eq!(error_counters.account_not_found, 0); assert_eq!(error_counters.account_not_found, 0);
assert_eq!(loaded_accounts.len(), 1); assert_eq!(loaded_accounts.len(), 1);
match &loaded_accounts[0] { match &loaded_accounts[0] {
Ok((instruction_accounts, instruction_loaders, instruction_credits)) => { Ok((
assert_eq!(instruction_accounts.len(), 1); transaction_accounts,
assert_eq!(instruction_accounts[0], accounts[0].1); transaction_loaders,
assert_eq!(instruction_loaders.len(), 2); transaction_credits,
assert_eq!(instruction_loaders[0].len(), 1); _transaction_rents,
assert_eq!(instruction_loaders[1].len(), 2); )) => {
assert_eq!(instruction_credits.len(), 1); assert_eq!(transaction_accounts.len(), 1);
assert_eq!(instruction_credits, &vec![0]); assert_eq!(transaction_accounts[0], accounts[0].1);
for loaders in instruction_loaders.iter() { assert_eq!(transaction_loaders.len(), 2);
assert_eq!(transaction_loaders[0].len(), 1);
assert_eq!(transaction_loaders[1].len(), 2);
assert_eq!(transaction_credits.len(), 1);
assert_eq!(transaction_credits, &vec![0]);
for loaders in transaction_loaders.iter() {
for (i, accounts_subset) in loaders.iter().enumerate() { for (i, accounts_subset) in loaders.iter().enumerate() {
// +1 to skip first not loader account // +1 to skip first not loader account
assert_eq![accounts_subset.1, accounts[i + 1].1]; assert_eq![accounts_subset.1, accounts[i + 1].1];
@ -1475,22 +1541,26 @@ mod tests {
let account1 = Account::new(2, 0, &Pubkey::default()); let account1 = Account::new(2, 0, &Pubkey::default());
let account2 = Account::new(3, 0, &Pubkey::default()); let account2 = Account::new(3, 0, &Pubkey::default());
let instruction_accounts0 = vec![account0, account2.clone()]; let transaction_accounts0 = vec![account0, account2.clone()];
let instruction_loaders0 = vec![]; let transaction_loaders0 = vec![];
let instruction_credits0 = vec![0, 2]; let transaction_credits0 = vec![0, 2];
let transaction_rents0 = vec![0, 0];
let loaded0 = Ok(( let loaded0 = Ok((
instruction_accounts0, transaction_accounts0,
instruction_loaders0, transaction_loaders0,
instruction_credits0, transaction_credits0,
transaction_rents0,
)); ));
let instruction_accounts1 = vec![account1, account2.clone()]; let transaction_accounts1 = vec![account1, account2.clone()];
let instruction_loaders1 = vec![]; let transaction_loaders1 = vec![];
let instruction_credits1 = vec![0, 3]; let transaction_credits1 = vec![0, 3];
let transaction_rents1 = vec![0, 0];
let loaded1 = Ok(( let loaded1 = Ok((
instruction_accounts1, transaction_accounts1,
instruction_loaders1, transaction_loaders1,
instruction_credits1, transaction_credits1,
transaction_rents1,
)); ));
let mut loaded = vec![loaded0, loaded1]; let mut loaded = vec![loaded0, loaded1];

View File

@ -30,7 +30,7 @@ use serde::de::{MapAccess, Visitor};
use serde::ser::{SerializeMap, Serializer}; use serde::ser::{SerializeMap, Serializer};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use solana_measure::measure::Measure; use solana_measure::measure::Measure;
use solana_sdk::account::{Account, LamportCredit}; use solana_sdk::account::Account;
use solana_sdk::pubkey::Pubkey; use solana_sdk::pubkey::Pubkey;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt; use std::fmt;
@ -76,9 +76,6 @@ pub struct AccountInfo {
} }
/// An offset into the AccountsDB::storage vector /// An offset into the AccountsDB::storage vector
pub type AppendVecId = usize; pub type AppendVecId = usize;
pub type InstructionAccounts = Vec<Account>;
pub type InstructionCredits = Vec<LamportCredit>;
pub type InstructionLoaders = Vec<Vec<(Pubkey, Account)>>;
// Each fork has a set of storage entries. // Each fork has a set of storage entries.
type ForkStores = HashMap<usize, Arc<AccountStorageEntry>>; type ForkStores = HashMap<usize, Arc<AccountStorageEntry>>;

View File

@ -1,8 +1,7 @@
use bincode::{deserialize_from, serialize_into, serialized_size}; use bincode::{deserialize_from, serialize_into, serialized_size};
use memmap::MmapMut; use memmap::MmapMut;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use solana_sdk::account::Account; use solana_sdk::{account::Account, pubkey::Pubkey, Epoch};
use solana_sdk::pubkey::Pubkey;
use std::fmt; use std::fmt;
use std::fs::{create_dir_all, remove_file, OpenOptions}; use std::fs::{create_dir_all, remove_file, OpenOptions};
use std::io; use std::io;
@ -38,6 +37,8 @@ pub struct AccountBalance {
pub owner: Pubkey, pub owner: Pubkey,
/// this account's data contains a loaded program (and is now read-only) /// this account's data contains a loaded program (and is now read-only)
pub executable: bool, pub executable: bool,
/// the epoch at which this account will next owe rent
pub rent_epoch: Epoch,
} }
/// References to Memory Mapped memory /// References to Memory Mapped memory
@ -57,6 +58,7 @@ impl<'a> StoredAccount<'a> {
lamports: self.balance.lamports, lamports: self.balance.lamports,
owner: self.balance.owner, owner: self.balance.owner,
executable: self.balance.executable, executable: self.balance.executable,
rent_epoch: self.balance.rent_epoch,
data: self.data.to_vec(), data: self.data.to_vec(),
} }
} }
@ -281,6 +283,7 @@ impl AppendVec {
lamports: account.lamports, lamports: account.lamports,
owner: account.owner, owner: account.owner,
executable: account.executable, executable: account.executable,
rent_epoch: account.rent_epoch,
}; };
let balance_ptr = &balance as *const AccountBalance; let balance_ptr = &balance as *const AccountBalance;
let data_len = storage_meta.data_len as usize; let data_len = storage_meta.data_len as usize;

View File

@ -2,23 +2,26 @@
//! programs. It offers a high-level API that signs transactions //! programs. It offers a high-level API that signs transactions
//! on behalf of the caller, and a low-level API for when they have //! on behalf of the caller, and a low-level API for when they have
//! already been signed and verified. //! already been signed and verified.
use crate::accounts::Accounts; use crate::{
use crate::accounts_db::{ accounts::{
AccountStorageEntry, AccountsDBSerialize, AppendVecId, ErrorCounters, InstructionAccounts, Accounts, TransactionAccounts, TransactionCredits, TransactionLoaders, TransactionRents,
InstructionCredits, InstructionLoaders, },
accounts_db::{AccountStorageEntry, AccountsDBSerialize, AppendVecId, ErrorCounters},
accounts_index::Fork,
blockhash_queue::BlockhashQueue,
epoch_schedule::EpochSchedule,
locked_accounts_results::LockedAccountsResults,
message_processor::{MessageProcessor, ProcessInstruction},
rent_collector::RentCollector,
serde_utils::{
deserialize_atomicbool, deserialize_atomicusize, serialize_atomicbool,
serialize_atomicusize,
},
stakes::Stakes,
status_cache::{SlotDelta, StatusCache},
storage_utils,
storage_utils::StorageAccounts,
}; };
use crate::accounts_index::Fork;
use crate::blockhash_queue::BlockhashQueue;
use crate::epoch_schedule::EpochSchedule;
use crate::locked_accounts_results::LockedAccountsResults;
use crate::message_processor::{MessageProcessor, ProcessInstruction};
use crate::serde_utils::{
deserialize_atomicbool, deserialize_atomicusize, serialize_atomicbool, serialize_atomicusize,
};
use crate::stakes::Stakes;
use crate::status_cache::{SlotDelta, StatusCache};
use crate::storage_utils;
use crate::storage_utils::StorageAccounts;
use bincode::{deserialize_from, serialize_into}; use bincode::{deserialize_from, serialize_into};
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
use log::*; use log::*;
@ -197,8 +200,11 @@ pub struct Bank {
/// The number of slots per Storage segment /// The number of slots per Storage segment
slots_per_segment: u64, slots_per_segment: u64,
/// Bank fork (i.e. slot, i.e. block) /// Bank slot (i.e. block)
slot: u64, slot: Slot,
/// Bank epoch
epoch: Epoch,
/// Bank height in term of banks /// Bank height in term of banks
bank_height: u64, bank_height: u64,
@ -214,6 +220,9 @@ pub struct Bank {
/// Latest transaction fees for transactions processed by this bank /// Latest transaction fees for transactions processed by this bank
fee_calculator: FeeCalculator, fee_calculator: FeeCalculator,
/// latest rent collector, knows the epoch
rent_collector: RentCollector,
/// initialized from genesis /// initialized from genesis
epoch_schedule: EpochSchedule, epoch_schedule: EpochSchedule,
@ -282,17 +291,22 @@ impl Bank {
let src = StatusCacheRc { let src = StatusCacheRc {
status_cache: parent.src.status_cache.clone(), status_cache: parent.src.status_cache.clone(),
}; };
let epoch_schedule = parent.epoch_schedule;
let epoch = epoch_schedule.get_epoch(slot);
let mut new = Bank { let mut new = Bank {
rc, rc,
src, src,
slot,
epoch,
blockhash_queue: RwLock::new(parent.blockhash_queue.read().unwrap().clone()), blockhash_queue: RwLock::new(parent.blockhash_queue.read().unwrap().clone()),
// TODO: clean this up, soo much special-case copying... // TODO: clean this up, soo much special-case copying...
ticks_per_slot: parent.ticks_per_slot, ticks_per_slot: parent.ticks_per_slot,
slots_per_segment: parent.slots_per_segment, slots_per_segment: parent.slots_per_segment,
slots_per_year: parent.slots_per_year, slots_per_year: parent.slots_per_year,
epoch_schedule: parent.epoch_schedule, epoch_schedule,
slot, rent_collector: parent.rent_collector.clone_with_epoch(epoch),
max_tick_height: (slot + 1) * parent.ticks_per_slot - 1, max_tick_height: (slot + 1) * parent.ticks_per_slot - 1,
bank_height: parent.bank_height + 1, bank_height: parent.bank_height + 1,
fee_calculator: FeeCalculator::new_derived( fee_calculator: FeeCalculator::new_derived(
@ -300,15 +314,15 @@ impl Bank {
parent.signature_count(), parent.signature_count(),
), ),
capitalization: AtomicUsize::new(parent.capitalization() as usize), capitalization: AtomicUsize::new(parent.capitalization() as usize),
inflation: parent.inflation.clone(), inflation: parent.inflation,
transaction_count: AtomicUsize::new(parent.transaction_count() as usize), transaction_count: AtomicUsize::new(parent.transaction_count() as usize),
stakes: RwLock::new(parent.stakes.read().unwrap().clone_with_epoch(0)), stakes: RwLock::new(parent.stakes.read().unwrap().clone_with_epoch(epoch)),
epoch_stakes: parent.epoch_stakes.clone(),
storage_accounts: RwLock::new(parent.storage_accounts.read().unwrap().clone()), storage_accounts: RwLock::new(parent.storage_accounts.read().unwrap().clone()),
parent_hash: parent.hash(), parent_hash: parent.hash(),
collector_id: *collector_id, collector_id: *collector_id,
collector_fees: AtomicUsize::new(0), collector_fees: AtomicUsize::new(0),
ancestors: HashMap::new(), ancestors: HashMap::new(),
epoch_stakes: HashMap::new(),
hash: RwLock::new(Hash::default()), hash: RwLock::new(Hash::default()),
is_delta: AtomicBool::new(false), is_delta: AtomicBool::new(false),
tick_height: AtomicUsize::new(parent.tick_height.load(Ordering::Relaxed)), tick_height: AtomicUsize::new(parent.tick_height.load(Ordering::Relaxed)),
@ -316,28 +330,21 @@ impl Bank {
message_processor: MessageProcessor::default(), message_processor: MessageProcessor::default(),
}; };
{
*new.stakes.write().unwrap() =
parent.stakes.read().unwrap().clone_with_epoch(new.epoch());
}
datapoint_info!( datapoint_info!(
"bank-new_from_parent-heights", "bank-new_from_parent-heights",
("slot_height", slot, i64), ("slot_height", slot, i64),
("bank_height", new.bank_height, i64) ("bank_height", new.bank_height, i64)
); );
new.epoch_stakes = { let stakers_epoch = epoch_schedule.get_stakers_epoch(slot);
let mut epoch_stakes = parent.epoch_stakes.clone(); // update epoch_stakes cache
let epoch = new.get_stakers_epoch(new.slot); // if my parent didn't populate for this staker's epoch, we've
// update epoch_vote_states cache // crossed a boundary
// if my parent didn't populate for this epoch, we've if new.epoch_stakes.get(&stakers_epoch).is_none() {
// crossed a boundary new.epoch_stakes
if epoch_stakes.get(&epoch).is_none() { .insert(stakers_epoch, new.stakes.read().unwrap().clone());
epoch_stakes.insert(epoch, new.stakes.read().unwrap().clone()); }
}
epoch_stakes
};
new.ancestors.insert(new.slot(), 0); new.ancestors.insert(new.slot(), 0);
new.parents().iter().enumerate().for_each(|(i, p)| { new.parents().iter().enumerate().for_each(|(i, p)| {
new.ancestors.insert(p.slot(), i + 1); new.ancestors.insert(p.slot(), i + 1);
@ -375,7 +382,7 @@ impl Bank {
} }
pub fn epoch(&self) -> u64 { pub fn epoch(&self) -> u64 {
self.epoch_schedule.get_epoch(self.slot) self.epoch
} }
pub fn freeze_lock(&self) -> RwLockReadGuard<Hash> { pub fn freeze_lock(&self) -> RwLockReadGuard<Hash> {
@ -611,7 +618,7 @@ impl Bank {
genesis_block.epoch_warmup, genesis_block.epoch_warmup,
); );
self.inflation = genesis_block.inflation.clone(); self.inflation = genesis_block.inflation;
// Add additional native programs specified in the genesis block // Add additional native programs specified in the genesis block
for (name, program_id) in &genesis_block.native_instruction_processors { for (name, program_id) in &genesis_block.native_instruction_processors {
@ -781,13 +788,21 @@ impl Bank {
txs: &[Transaction], txs: &[Transaction],
results: Vec<Result<()>>, results: Vec<Result<()>>,
error_counters: &mut ErrorCounters, error_counters: &mut ErrorCounters,
) -> Vec<Result<(InstructionAccounts, InstructionLoaders, InstructionCredits)>> { ) -> Vec<
Result<(
TransactionAccounts,
TransactionLoaders,
TransactionCredits,
TransactionRents,
)>,
> {
self.rc.accounts.load_accounts( self.rc.accounts.load_accounts(
&self.ancestors, &self.ancestors,
txs, txs,
results, results,
&self.blockhash_queue.read().unwrap(), &self.blockhash_queue.read().unwrap(),
error_counters, error_counters,
&self.rent_collector,
) )
} }
fn check_refs( fn check_refs(
@ -932,7 +947,14 @@ impl Bank {
lock_results: &LockedAccountsResults, lock_results: &LockedAccountsResults,
max_age: usize, max_age: usize,
) -> ( ) -> (
Vec<Result<(InstructionAccounts, InstructionLoaders, InstructionCredits)>>, Vec<
Result<(
TransactionAccounts,
TransactionLoaders,
TransactionCredits,
TransactionRents,
)>,
>,
Vec<Result<()>>, Vec<Result<()>>,
Vec<usize>, Vec<usize>,
usize, usize,
@ -970,7 +992,7 @@ impl Bank {
.zip(txs.iter()) .zip(txs.iter())
.map(|(accs, tx)| match accs { .map(|(accs, tx)| match accs {
Err(e) => Err(e.clone()), Err(e) => Err(e.clone()),
Ok((ref mut accounts, ref mut loaders, ref mut credits)) => { Ok((ref mut accounts, ref mut loaders, ref mut credits, ref mut _rents)) => {
signature_count += tx.message().header.num_required_signatures as usize; signature_count += tx.message().header.num_required_signatures as usize;
self.message_processor self.message_processor
.process_message(tx.message(), loaders, accounts, credits) .process_message(tx.message(), loaders, accounts, credits)
@ -1061,9 +1083,10 @@ impl Bank {
&self, &self,
txs: &[Transaction], txs: &[Transaction],
loaded_accounts: &mut [Result<( loaded_accounts: &mut [Result<(
InstructionAccounts, TransactionAccounts,
InstructionLoaders, TransactionLoaders,
InstructionCredits, TransactionCredits,
TransactionRents,
)>], )>],
executed: &[Result<()>], executed: &[Result<()>],
tx_count: usize, tx_count: usize,
@ -1332,7 +1355,12 @@ impl Bank {
&self, &self,
txs: &[Transaction], txs: &[Transaction],
res: &[Result<()>], res: &[Result<()>],
loaded: &[Result<(InstructionAccounts, InstructionLoaders, InstructionCredits)>], loaded: &[Result<(
TransactionAccounts,
TransactionLoaders,
TransactionCredits,
TransactionRents,
)>],
) { ) {
for (i, raccs) in loaded.iter().enumerate() { for (i, raccs) in loaded.iter().enumerate() {
if res[i].is_err() || raccs.is_err() { if res[i].is_err() || raccs.is_err() {

View File

@ -12,6 +12,7 @@ pub mod loader_utils;
pub mod locked_accounts_results; pub mod locked_accounts_results;
pub mod message_processor; pub mod message_processor;
mod native_loader; mod native_loader;
pub mod rent_collector;
mod serde_utils; mod serde_utils;
pub mod stakes; pub mod stakes;
pub mod status_cache; pub mod status_cache;

View File

@ -0,0 +1,65 @@
//! calculate and collect rent from Accounts
use crate::epoch_schedule::EpochSchedule;
use solana_sdk::{account::Account, rent::Rent, timing::Epoch};
#[derive(Default, Serialize, Deserialize, Clone)]
pub struct RentCollector {
pub epoch: Epoch,
pub epoch_schedule: EpochSchedule,
pub slots_per_year: f64,
pub rent: Rent,
}
impl RentCollector {
pub fn new(
epoch: Epoch,
epoch_schedule: &EpochSchedule,
slots_per_year: f64,
rent: &Rent,
) -> Self {
Self {
epoch,
epoch_schedule: *epoch_schedule,
slots_per_year,
rent: *rent,
}
}
pub fn clone_with_epoch(&self, epoch: Epoch) -> Self {
Self {
epoch,
..self.clone()
}
}
// updates this account's lamports and status and returns
// the account rent collected, if any
//
pub fn update(&self, mut account: Account) -> Option<(Account, u64)> {
if account.data.is_empty() || account.rent_epoch > self.epoch {
Some((account, 0))
} else {
let slots_elapsed: u64 = (account.rent_epoch..=self.epoch)
.map(|epoch| self.epoch_schedule.get_slots_in_epoch(epoch + 1))
.sum();
let (rent_due, exempt) = self.rent.due(
account.lamports,
account.data.len(),
slots_elapsed as f64 / self.slots_per_year,
);
if exempt || rent_due != 0 {
if account.lamports > rent_due {
account.rent_epoch = self.epoch + 1;
account.lamports -= rent_due;
Some((account, rent_due))
} else {
None
}
} else {
// maybe collect rent later, leave account alone
Some((account, 0))
}
}
}
}

View File

@ -247,10 +247,8 @@ mod tests {
let populated_key = Pubkey::new_rand(); let populated_key = Pubkey::new_rand();
let mut populated_account = Account { let mut populated_account = Account {
lamports: 0,
data: vec![0, 1, 2, 3], data: vec![0, 1, 2, 3],
owner: Pubkey::default(), ..Account::default()
executable: false,
}; };
let unchanged_account = populated_account.clone(); let unchanged_account = populated_account.clone();

View File

@ -1,4 +1,5 @@
use crate::pubkey::Pubkey; use crate::pubkey::Pubkey;
use crate::Epoch;
use std::{cmp, fmt}; use std::{cmp, fmt};
/// An Account with data that is stored on chain /// An Account with data that is stored on chain
@ -13,6 +14,8 @@ pub struct Account {
pub owner: Pubkey, pub owner: Pubkey,
/// this account's data contains a loaded program (and is now read-only) /// this account's data contains a loaded program (and is now read-only)
pub executable: bool, pub executable: bool,
/// the epoch at which this account will next owe rent
pub rent_epoch: Epoch,
} }
impl fmt::Debug for Account { impl fmt::Debug for Account {
@ -25,11 +28,12 @@ impl fmt::Debug for Account {
}; };
write!( write!(
f, f,
"Account {{ lamports: {} data.len: {} owner: {} executable: {}{} }}", "Account {{ lamports: {} data.len: {} owner: {} executable: {} rent_epoch: {}{} }}",
self.lamports, self.lamports,
self.data.len(), self.data.len(),
self.owner, self.owner,
self.executable, self.executable,
self.rent_epoch,
data_str, data_str,
) )
} }
@ -42,7 +46,7 @@ impl Account {
lamports, lamports,
data: vec![0u8; space], data: vec![0u8; space],
owner: *owner, owner: *owner,
executable: false, ..Account::default()
} }
} }
@ -56,7 +60,7 @@ impl Account {
lamports, lamports,
data, data,
owner: *owner, owner: *owner,
executable: false, ..Account::default()
}) })
} }

View File

@ -29,7 +29,7 @@ pub struct FeeCalculator {
pub const DEFAULT_TARGET_LAMPORTS_PER_SIGNATURE: u64 = 42; pub const DEFAULT_TARGET_LAMPORTS_PER_SIGNATURE: u64 = 42;
pub const DEFAULT_TARGET_SIGNATURES_PER_SLOT: usize = pub const DEFAULT_TARGET_SIGNATURES_PER_SLOT: usize =
710_000 * DEFAULT_TICKS_PER_SLOT as usize / DEFAULT_TICKS_PER_SECOND as usize; 710_000 * DEFAULT_TICKS_PER_SLOT as usize / DEFAULT_TICKS_PER_SECOND as usize;
pub const DEFAULT_BURN_PERCENT: u8 = 127; pub const DEFAULT_BURN_PERCENT: u8 = ((50usize * std::u8::MAX as usize) / 100usize) as u8;
impl Default for FeeCalculator { impl Default for FeeCalculator {
fn default() -> Self { fn default() -> Self {

View File

@ -1,6 +1,6 @@
//! configuration for network inflation //! configuration for network inflation
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)] #[derive(Serialize, Deserialize, PartialEq, Clone, Debug, Copy)]
pub struct Inflation { pub struct Inflation {
/// Initial inflation percentage, from time=0 /// Initial inflation percentage, from time=0
pub initial: f64, pub initial: f64,

View File

@ -28,3 +28,6 @@ pub mod transport;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
pub type Epoch = u64;
pub type Slot = u64;

View File

@ -14,5 +14,6 @@ pub fn create_loadable_account(name: &str) -> Account {
owner: id(), owner: id(),
data: name.as_bytes().to_vec(), data: name.as_bytes().to_vec(),
executable: true, executable: true,
rent_epoch: 0,
} }
} }

View File

@ -1,12 +1,15 @@
//! configuration for network rent //! configuration for network rent
#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)] #[derive(Serialize, Deserialize, PartialEq, Clone, Copy, Debug)]
pub struct Rent { pub struct Rent {
/// Rental rate /// Rental rate
pub lamports_per_byte_year: u64, pub lamports_per_byte_year: u64,
/// exemption threshold, in years /// exemption threshold, in years
pub exemption_threshold: f64, 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: /// default rental rate in lamports/byte-year, based on:
@ -14,16 +17,20 @@ pub struct Rent {
/// $1 per Sol /// $1 per Sol
/// $0.01 per megabyte day /// $0.01 per megabyte day
/// $3.65 per megabyte year /// $3.65 per megabyte year
pub const DEFAULT_LAMPORTS_PER_BYTE_YEAR: u64 = 17_179_869_184 / 100 * 365 / (1024 * 1024); 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 /// default amount of time (in years) the balance has to include rent for
pub const DEFAULT_EXEMPTION_THRESHOLD: f64 = 2.0; 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 Rent {
fn default() -> Self { fn default() -> Self {
Self { Self {
lamports_per_byte_year: DEFAULT_LAMPORTS_PER_BYTE_YEAR, lamports_per_byte_year: DEFAULT_LAMPORTS_PER_BYTE_YEAR,
exemption_threshold: DEFAULT_EXEMPTION_THRESHOLD, exemption_threshold: DEFAULT_EXEMPTION_THRESHOLD,
burn_percent: DEFAULT_BURN_PERCENT,
} }
} }
} }
@ -41,12 +48,14 @@ impl Rent {
} }
/// rent due on account's data_len with balance /// rent due on account's data_len with balance
pub fn due(&self, balance: u64, data_len: usize, years_elapsed: f64) -> u64 { pub fn due(&self, balance: u64, data_len: usize, years_elapsed: f64) -> (u64, bool) {
if self.is_exempt(balance, data_len) { if self.is_exempt(balance, data_len) {
0 (0, true)
} else { } else {
let bytes = data_len as u64; (
((self.lamports_per_byte_year * bytes) as f64 * years_elapsed) as u64 ((self.lamports_per_byte_year * data_len as u64) as f64 * years_elapsed) as u64,
false,
)
} }
} }
} }
@ -59,14 +68,63 @@ mod tests {
fn test_due() { fn test_due() {
let rent = Rent::default(); let rent = Rent::default();
assert_eq!(rent.due(0, 1, 1.0), DEFAULT_LAMPORTS_PER_BYTE_YEAR); assert_eq!(
rent.due(0, 1, 1.0),
(
DEFAULT_LAMPORTS_PER_BYTE_YEAR,
DEFAULT_LAMPORTS_PER_BYTE_YEAR == 0
)
);
assert_eq!( assert_eq!(
rent.due( rent.due(
DEFAULT_LAMPORTS_PER_BYTE_YEAR * DEFAULT_EXEMPTION_THRESHOLD as u64, DEFAULT_LAMPORTS_PER_BYTE_YEAR * DEFAULT_EXEMPTION_THRESHOLD as u64,
1, 1,
1.0 1.0
), ),
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,
// );
// }
} }