Pay rent to validators (#7164)

automerge
This commit is contained in:
Parth
2019-12-05 19:40:34 +05:30
committed by Grimes
parent 709bda5939
commit 0d1fed78af
2 changed files with 256 additions and 8 deletions

View File

@ -44,6 +44,7 @@ use solana_sdk::{
timing::years_as_slots,
transaction::{Result, Transaction, TransactionError},
};
use solana_vote_program::vote_state::VoteState;
use std::{
collections::HashMap,
io::{BufReader, Cursor, Error as IOError, Read},
@ -1210,16 +1211,49 @@ impl Bank {
}
}
fn distribute_rent_to_validators(
&self,
vote_account_hashmap: &HashMap<Pubkey, (u64, Account)>,
rent_to_be_distributed: u64,
) {
let mut total_staked = 0;
let node_stakes = vote_account_hashmap
.iter()
.filter_map(|(_vote_pubkey, (staked, account))| {
total_staked += *staked;
VoteState::deserialize(&account.data)
.ok()
.map(|vote_state| (vote_state.node_pubkey, *staked))
.filter(|(_pubkey, staked)| *staked != 0)
})
.collect::<Vec<(Pubkey, u64)>>();
node_stakes.iter().for_each(|(pubkey, staked)| {
let rent_to_be_paid =
(((staked * rent_to_be_distributed) as f64) / (total_staked as f64)) as u64;
let mut account = self.get_account(pubkey).unwrap_or_default();
account.lamports += rent_to_be_paid;
self.store_account(pubkey, &account);
});
}
fn distribute_rent(&self) {
let total_rent_collected = self.collected_rent.load(Ordering::Relaxed);
if total_rent_collected != 0 {
let burned_portion =
(total_rent_collected * u64::from(self.rent_collector.rent.burn_percent)) / 100;
let _rent_to_be_distributed = total_rent_collected - burned_portion;
// TODO: distribute remaining rent amount to validators
// self.capitalization.fetch_sub(burned_portion, Ordering::Relaxed);
let (burned_portion, rent_to_be_distributed) = self
.rent_collector
.rent
.calculate_burn(total_rent_collected);
self.capitalization
.fetch_sub(burned_portion, Ordering::Relaxed);
if rent_to_be_distributed == 0 {
return;
}
self.distribute_rent_to_validators(&self.vote_accounts(), rent_to_be_distributed);
}
fn collect_rent(&self, res: &[Result<()>], loaded_accounts: &[Result<TransactionLoadResult>]) {
@ -1680,10 +1714,10 @@ mod tests {
poh_config::PohConfig,
rent::Rent,
signature::{Keypair, KeypairUtil},
system_instruction,
system_instruction, system_program,
sysvar::{fees::Fees, rewards::Rewards},
};
use solana_stake_program::stake_state::{Authorized, Delegation, Lockup, Stake};
use solana_stake_program::stake_state::{self, Authorized, Delegation, Lockup, Stake};
use solana_vote_program::{
vote_instruction,
vote_state::{self, Vote, VoteInit, VoteState, MAX_LOCKOUT_HISTORY},
@ -2054,6 +2088,215 @@ mod tests {
bank
}
#[test]
fn test_rent_distribution() {
let bootstrap_leader_pubkey = Pubkey::new_rand();
let bootstrap_leader_stake_lamports = 30;
let mut genesis_config = create_genesis_config_with_leader(
10,
&bootstrap_leader_pubkey,
bootstrap_leader_stake_lamports,
)
.genesis_config;
genesis_config.epoch_schedule = EpochSchedule::custom(
MINIMUM_SLOTS_PER_EPOCH,
genesis_config.epoch_schedule.leader_schedule_slot_offset,
false,
);
genesis_config.rent = Rent {
lamports_per_byte_year: 1,
exemption_threshold: 2.0,
burn_percent: 10,
};
let rent = Rent::free();
let validator_1_pubkey = Pubkey::new_rand();
let validator_1_stake_lamports = 23;
let validator_1_staking_keypair = Keypair::new();
let validator_1_voting_keypair = Keypair::new();
let validator_1_vote_account = vote_state::create_account(
&validator_1_voting_keypair.pubkey(),
&validator_1_pubkey,
0,
validator_1_stake_lamports,
);
let validator_1_stake_account = stake_state::create_account(
&validator_1_staking_keypair.pubkey(),
&validator_1_voting_keypair.pubkey(),
&validator_1_vote_account,
&rent,
validator_1_stake_lamports,
);
genesis_config.accounts.insert(
validator_1_pubkey,
Account::new(42, 0, &system_program::id()),
);
genesis_config.accounts.insert(
validator_1_staking_keypair.pubkey(),
validator_1_stake_account,
);
genesis_config.accounts.insert(
validator_1_voting_keypair.pubkey(),
validator_1_vote_account,
);
let validator_2_pubkey = Pubkey::new_rand();
let validator_2_stake_lamports = 22;
let validator_2_staking_keypair = Keypair::new();
let validator_2_voting_keypair = Keypair::new();
let validator_2_vote_account = vote_state::create_account(
&validator_2_voting_keypair.pubkey(),
&validator_2_pubkey,
0,
validator_2_stake_lamports,
);
let validator_2_stake_account = stake_state::create_account(
&validator_2_staking_keypair.pubkey(),
&validator_2_voting_keypair.pubkey(),
&validator_2_vote_account,
&rent,
validator_2_stake_lamports,
);
genesis_config.accounts.insert(
validator_2_pubkey,
Account::new(42, 0, &system_program::id()),
);
genesis_config.accounts.insert(
validator_2_staking_keypair.pubkey(),
validator_2_stake_account,
);
genesis_config.accounts.insert(
validator_2_voting_keypair.pubkey(),
validator_2_vote_account,
);
let validator_3_pubkey = Pubkey::new_rand();
let validator_3_stake_lamports = 25;
let validator_3_staking_keypair = Keypair::new();
let validator_3_voting_keypair = Keypair::new();
let validator_3_vote_account = vote_state::create_account(
&validator_3_voting_keypair.pubkey(),
&validator_3_pubkey,
0,
validator_3_stake_lamports,
);
let validator_3_stake_account = stake_state::create_account(
&validator_3_staking_keypair.pubkey(),
&validator_3_voting_keypair.pubkey(),
&validator_3_vote_account,
&rent,
validator_3_stake_lamports,
);
genesis_config.accounts.insert(
validator_3_pubkey,
Account::new(42, 0, &system_program::id()),
);
genesis_config.accounts.insert(
validator_3_staking_keypair.pubkey(),
validator_3_stake_account,
);
genesis_config.accounts.insert(
validator_3_voting_keypair.pubkey(),
validator_3_vote_account,
);
genesis_config.rent = Rent {
lamports_per_byte_year: 1,
exemption_threshold: 10.0,
burn_percent: 10,
};
let mut bank = Bank::new(&genesis_config);
// Enable rent collection
bank.rent_collector.epoch = 5;
bank.rent_collector.slots_per_year = 192.0;
let payer = Keypair::new();
let payer_account = Account::new(400, 2, &system_program::id());
bank.store_account(&payer.pubkey(), &payer_account);
let payee = Keypair::new();
let payee_account = Account::new(70, 1, &system_program::id());
bank.store_account(&payee.pubkey(), &payee_account);
let bootstrap_leader_initial_balance = bank.get_balance(&bootstrap_leader_pubkey);
let tx = system_transaction::transfer(&payer, &payee.pubkey(), 180, genesis_config.hash());
let result = bank.process_transaction(&tx);
assert_eq!(result, Ok(()));
let mut total_rent_deducted = 0;
// 400 - 130(Rent) - 180(Transfer)
assert_eq!(bank.get_balance(&payer.pubkey()), 90);
total_rent_deducted += 130;
// 70 - 70(Rent) + 180(Transfer) - 21(Rent)
assert_eq!(bank.get_balance(&payee.pubkey()), 159);
total_rent_deducted += 70 + 21;
let previous_capitalization = bank.capitalization.load(Ordering::Relaxed);
bank.freeze();
assert_eq!(
bank.collected_rent.load(Ordering::Relaxed),
total_rent_deducted
);
let burned_portion =
total_rent_deducted * u64::from(bank.rent_collector.rent.burn_percent) / 100;
let rent_to_be_distributed = total_rent_deducted - burned_portion;
let bootstrap_leader_portion =
((bootstrap_leader_stake_lamports * rent_to_be_distributed) as f64 / 100.0) as u64;
assert_eq!(
bank.get_balance(&bootstrap_leader_pubkey),
bootstrap_leader_portion + bootstrap_leader_initial_balance
);
let validator_1_portion =
((validator_1_stake_lamports * rent_to_be_distributed) as f64 / 100.0) as u64;
assert_eq!(
bank.get_balance(&validator_1_pubkey),
validator_1_portion + 42
);
let validator_2_portion =
((validator_2_stake_lamports * rent_to_be_distributed) as f64 / 100.0) as u64;
assert_eq!(
bank.get_balance(&validator_2_pubkey),
validator_2_portion + 42
);
let validator_3_portion =
((validator_3_stake_lamports * rent_to_be_distributed) as f64 / 100.0) as u64;
assert_eq!(
bank.get_balance(&validator_3_pubkey),
validator_3_portion + 42
);
let current_capitalization = bank.capitalization.load(Ordering::Relaxed);
assert_eq!(
previous_capitalization - current_capitalization,
burned_portion
);
}
#[test]
#[allow(clippy::cognitive_complexity)]
fn test_rent() {

View File

@ -40,6 +40,11 @@ impl Default for Rent {
}
impl Rent {
/// calculate how much rent to burn from the collected rent
pub fn calculate_burn(&self, rent_collected: u64) -> (u64, u64) {
let burned_portion = (rent_collected * u64::from(self.burn_percent)) / 100;
(burned_portion, rent_collected - burned_portion)
}
/// 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;