@@ -650,15 +650,5 @@ mod tests {
|
||||
.sum::<u64>();
|
||||
|
||||
assert_eq!(issued_lamports, lamports);
|
||||
|
||||
genesis_config
|
||||
.accounts
|
||||
.sort_by(|(ka, _), (kb, _)| ka.cmp(kb));
|
||||
|
||||
let len = genesis_config.accounts.len();
|
||||
genesis_config
|
||||
.accounts
|
||||
.dedup_by(|(ka, _), (kb, _)| ka == kb);
|
||||
assert_eq!(genesis_config.accounts.len(), len);
|
||||
}
|
||||
}
|
||||
|
@@ -28,7 +28,15 @@ use solana_sdk::{
|
||||
use solana_stake_program::stake_state;
|
||||
use solana_storage_program::storage_contract;
|
||||
use solana_vote_program::vote_state;
|
||||
use std::{collections::HashMap, error, fs::File, io, path::PathBuf, str::FromStr, time::Duration};
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
error,
|
||||
fs::File,
|
||||
io,
|
||||
path::PathBuf,
|
||||
str::FromStr,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
pub enum AccountFileFormat {
|
||||
Pubkey,
|
||||
@@ -344,7 +352,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
||||
bootstrap_leader_stake_lamports,
|
||||
);
|
||||
|
||||
let mut accounts = vec![
|
||||
let mut accounts: BTreeMap<Pubkey, Account> = [
|
||||
// node needs an account to issue votes from
|
||||
(
|
||||
bootstrap_leader_pubkey,
|
||||
@@ -354,13 +362,16 @@ fn main() -> Result<(), Box<dyn error::Error>> {
|
||||
(bootstrap_vote_pubkey, bootstrap_leader_vote_account),
|
||||
// bootstrap leader stake
|
||||
(bootstrap_stake_pubkey, bootstrap_leader_stake_account),
|
||||
];
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
if let Some(bootstrap_storage_pubkey) = bootstrap_storage_pubkey {
|
||||
accounts.push((
|
||||
accounts.insert(
|
||||
bootstrap_storage_pubkey,
|
||||
storage_contract::create_validator_storage_account(bootstrap_leader_pubkey, 1),
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
let ticks_per_slot = value_t_or_exit!(matches, "ticks_per_slot", u64);
|
||||
@@ -539,27 +550,28 @@ mod tests {
|
||||
assert_eq!(genesis_config.accounts.len(), genesis_accounts.len());
|
||||
|
||||
// Test account data matches
|
||||
(0..genesis_accounts.len()).for_each(|i| {
|
||||
for (pubkey_str, b64_account) in genesis_accounts.iter() {
|
||||
let pubkey = pubkey_str.parse().unwrap();
|
||||
assert_eq!(
|
||||
genesis_accounts[&genesis_config.accounts[i].0.to_string()].owner,
|
||||
genesis_config.accounts[i].1.owner.to_string()
|
||||
b64_account.owner,
|
||||
genesis_config.accounts[&pubkey].owner.to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
genesis_accounts[&genesis_config.accounts[i].0.to_string()].balance,
|
||||
genesis_config.accounts[i].1.lamports
|
||||
b64_account.balance,
|
||||
genesis_config.accounts[&pubkey].lamports
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
genesis_accounts[&genesis_config.accounts[i].0.to_string()].executable,
|
||||
genesis_config.accounts[i].1.executable
|
||||
b64_account.executable,
|
||||
genesis_config.accounts[&pubkey].executable
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
genesis_accounts[&genesis_config.accounts[i].0.to_string()].data,
|
||||
base64::encode(&genesis_config.accounts[i].1.data)
|
||||
b64_account.data,
|
||||
base64::encode(&genesis_config.accounts[&pubkey].data)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Test more accounts can be appended
|
||||
@@ -612,54 +624,37 @@ mod tests {
|
||||
);
|
||||
|
||||
// Test old accounts are still there
|
||||
(0..genesis_accounts.len()).for_each(|i| {
|
||||
for (pubkey_str, b64_account) in genesis_accounts.iter() {
|
||||
let pubkey = &pubkey_str.parse().unwrap();
|
||||
assert_eq!(
|
||||
genesis_accounts[&genesis_config.accounts[i].0.to_string()].balance,
|
||||
genesis_config.accounts[i].1.lamports,
|
||||
b64_account.balance,
|
||||
genesis_config.accounts[&pubkey].lamports,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Test new account data matches
|
||||
(0..genesis_accounts1.len()).for_each(|i| {
|
||||
for (pubkey_str, b64_account) in genesis_accounts1.iter() {
|
||||
let pubkey = pubkey_str.parse().unwrap();
|
||||
assert_eq!(
|
||||
genesis_accounts1[&genesis_config.accounts[genesis_accounts.len() + i]
|
||||
.0
|
||||
.to_string()]
|
||||
.owner,
|
||||
genesis_config.accounts[genesis_accounts.len() + i]
|
||||
.1
|
||||
.owner
|
||||
.to_string(),
|
||||
b64_account.owner,
|
||||
genesis_config.accounts[&pubkey].owner.to_string()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
genesis_accounts1[&genesis_config.accounts[genesis_accounts.len() + i]
|
||||
.0
|
||||
.to_string()]
|
||||
.balance,
|
||||
genesis_config.accounts[genesis_accounts.len() + i]
|
||||
.1
|
||||
.lamports,
|
||||
b64_account.balance,
|
||||
genesis_config.accounts[&pubkey].lamports,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
genesis_accounts1[&genesis_config.accounts[genesis_accounts.len() + i]
|
||||
.0
|
||||
.to_string()]
|
||||
.executable,
|
||||
genesis_config.accounts[genesis_accounts.len() + i]
|
||||
.1
|
||||
.executable,
|
||||
b64_account.executable,
|
||||
genesis_config.accounts[&pubkey].executable,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
genesis_accounts1[&genesis_config.accounts[genesis_accounts.len() + i]
|
||||
.0
|
||||
.to_string()]
|
||||
.data,
|
||||
base64::encode(&genesis_config.accounts[genesis_accounts.len() + i].1.data),
|
||||
b64_account.data,
|
||||
base64::encode(&genesis_config.accounts[&pubkey].data),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Test accounts from keypairs can be appended
|
||||
let account_keypairs: Vec<_> = (0..3).map(|_| Keypair::new()).collect();
|
||||
@@ -714,89 +709,60 @@ mod tests {
|
||||
);
|
||||
|
||||
// Test old accounts are still there
|
||||
(0..genesis_accounts.len()).for_each(|i| {
|
||||
for (pubkey_str, b64_account) in genesis_accounts {
|
||||
let pubkey = pubkey_str.parse().unwrap();
|
||||
assert_eq!(
|
||||
genesis_accounts[&genesis_config.accounts[i].0.to_string()].balance,
|
||||
genesis_config.accounts[i].1.lamports,
|
||||
b64_account.balance,
|
||||
genesis_config.accounts[&pubkey].lamports,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Test new account data matches
|
||||
(0..genesis_accounts1.len()).for_each(|i| {
|
||||
for (pubkey_str, b64_account) in genesis_accounts1 {
|
||||
let pubkey = pubkey_str.parse().unwrap();
|
||||
assert_eq!(
|
||||
genesis_accounts1[&genesis_config.accounts[genesis_accounts.len() + i]
|
||||
.0
|
||||
.to_string()]
|
||||
.owner,
|
||||
genesis_config.accounts[genesis_accounts.len() + i]
|
||||
.1
|
||||
.owner
|
||||
.to_string(),
|
||||
b64_account.owner,
|
||||
genesis_config.accounts[&pubkey].owner.to_string(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
genesis_accounts1[&genesis_config.accounts[genesis_accounts.len() + i]
|
||||
.0
|
||||
.to_string()]
|
||||
.balance,
|
||||
genesis_config.accounts[genesis_accounts.len() + i]
|
||||
.1
|
||||
.lamports,
|
||||
b64_account.balance,
|
||||
genesis_config.accounts[&pubkey].lamports,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
genesis_accounts1[&genesis_config.accounts[genesis_accounts.len() + i]
|
||||
.0
|
||||
.to_string()]
|
||||
.executable,
|
||||
genesis_config.accounts[genesis_accounts.len() + i]
|
||||
.1
|
||||
.executable,
|
||||
b64_account.executable,
|
||||
genesis_config.accounts[&pubkey].executable,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
genesis_accounts1[&genesis_config.accounts[genesis_accounts.len() + i]
|
||||
.0
|
||||
.to_string()]
|
||||
.data,
|
||||
base64::encode(&genesis_config.accounts[genesis_accounts.len() + i].1.data),
|
||||
b64_account.data,
|
||||
base64::encode(&genesis_config.accounts[&pubkey].data),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
let offset = genesis_accounts.len() + genesis_accounts1.len();
|
||||
// Test account data for keypairs matches
|
||||
account_keypairs.iter().for_each(|keypair| {
|
||||
let mut i = 0;
|
||||
(offset..(offset + account_keypairs.len())).for_each(|n| {
|
||||
if keypair.pubkey() == genesis_config.accounts[n].0 {
|
||||
i = n;
|
||||
}
|
||||
});
|
||||
|
||||
assert_ne!(i, 0);
|
||||
|
||||
let keypair_str = serde_json::to_string(&keypair.to_bytes().to_vec()).unwrap();
|
||||
let pubkey = keypair.pubkey();
|
||||
assert_eq!(
|
||||
genesis_accounts2[&serde_json::to_string(&keypair.to_bytes().to_vec()).unwrap()]
|
||||
.owner,
|
||||
genesis_config.accounts[i].1.owner.to_string(),
|
||||
genesis_accounts2[&keypair_str].owner,
|
||||
genesis_config.accounts[&pubkey].owner.to_string(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
genesis_accounts2[&serde_json::to_string(&keypair.to_bytes().to_vec()).unwrap()]
|
||||
.balance,
|
||||
genesis_config.accounts[i].1.lamports,
|
||||
genesis_accounts2[&keypair_str].balance,
|
||||
genesis_config.accounts[&pubkey].lamports,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
genesis_accounts2[&serde_json::to_string(&keypair.to_bytes().to_vec()).unwrap()]
|
||||
.executable,
|
||||
genesis_config.accounts[i].1.executable,
|
||||
genesis_accounts2[&keypair_str].executable,
|
||||
genesis_config.accounts[&pubkey].executable,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
genesis_accounts2[&serde_json::to_string(&keypair.to_bytes().to_vec()).unwrap()]
|
||||
.data,
|
||||
base64::encode(&genesis_config.accounts[i].1.data),
|
||||
genesis_accounts2[&keypair_str].data,
|
||||
base64::encode(&genesis_config.accounts[&pubkey].data),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@@ -21,17 +21,16 @@ pub struct StakerInfo {
|
||||
}
|
||||
|
||||
// lamports required to run staking operations for one year
|
||||
// the staker account needs to be rent exempt *and* carry enough
|
||||
// the staker account needs carry enough
|
||||
// lamports to cover TX fees (delegation) for one year,
|
||||
// and we support one delegation per epoch
|
||||
fn calculate_staker_lamports(genesis_config: &GenesisConfig) -> u64 {
|
||||
genesis_config.rent.minimum_balance(0).max(1)
|
||||
+ genesis_config.fee_calculator.max_lamports_per_signature
|
||||
* genesis_config.epoch_schedule.get_epoch(years_as_slots(
|
||||
1.0,
|
||||
&genesis_config.poh_config.target_tick_duration,
|
||||
genesis_config.ticks_per_slot,
|
||||
) as Slot)
|
||||
fn calculate_staker_fees(genesis_config: &GenesisConfig, years: f64) -> u64 {
|
||||
genesis_config.fee_calculator.max_lamports_per_signature
|
||||
* genesis_config.epoch_schedule.get_epoch(years_as_slots(
|
||||
years,
|
||||
&genesis_config.poh_config.target_tick_duration,
|
||||
genesis_config.ticks_per_slot,
|
||||
) as Slot)
|
||||
}
|
||||
|
||||
/// create stake accounts for lamports with at most stake_granularity in each
|
||||
@@ -53,19 +52,23 @@ pub fn create_and_add_stakes(
|
||||
|
||||
let total_lamports = sol_to_lamports(staker_info.sol);
|
||||
|
||||
let staker_lamports = calculate_staker_lamports(genesis_config);
|
||||
let staker_account = (
|
||||
authorized.staker,
|
||||
Account::new(staker_lamports, 0, &system_program::id()),
|
||||
);
|
||||
let staker_rent_reserve = get_stake_rent_exempt_reserve(&genesis_config.rent).max(1);
|
||||
let staker_fees = calculate_staker_fees(genesis_config, 1.0);
|
||||
|
||||
let stakes_lamports = if !genesis_config.accounts.contains(&staker_account) {
|
||||
genesis_config.accounts.push(staker_account);
|
||||
let mut stakes_lamports = total_lamports - staker_fees;
|
||||
|
||||
total_lamports - staker_lamports
|
||||
} else {
|
||||
total_lamports
|
||||
};
|
||||
// lamports required to run staking operations for one year
|
||||
// the staker account needs to be rent exempt *and* carry enough
|
||||
// lamports to cover TX fees (delegation) for one year,
|
||||
// and we support one delegation per epoch
|
||||
genesis_config
|
||||
.accounts
|
||||
.entry(authorized.staker)
|
||||
.or_insert_with(|| {
|
||||
stakes_lamports -= staker_rent_reserve;
|
||||
Account::new(staker_rent_reserve, 0, &system_program::id())
|
||||
})
|
||||
.lamports += staker_fees;
|
||||
|
||||
// the staker account needs to be rent exempt *and* carry enough
|
||||
// lamports to cover TX fees (delegation) for one year
|
||||
@@ -150,11 +153,10 @@ mod tests {
|
||||
granularity: u64,
|
||||
len: usize,
|
||||
) {
|
||||
assert!(
|
||||
total_lamports
|
||||
== create_and_add_stakes(genesis_config, staker_info, unlock_info, granularity)
|
||||
assert_eq!(
|
||||
total_lamports,
|
||||
create_and_add_stakes(genesis_config, staker_info, unlock_info, granularity)
|
||||
);
|
||||
|
||||
assert_eq!(genesis_config.accounts.len(), len);
|
||||
assert_eq!(
|
||||
genesis_config
|
||||
@@ -169,7 +171,7 @@ mod tests {
|
||||
.iter()
|
||||
.all(|(_pubkey, account)| account.lamports <= granularity
|
||||
|| account.lamports - granularity
|
||||
< get_stake_rent_exempt_reserve(&genesis_config.rent)));
|
||||
<= get_stake_rent_exempt_reserve(&genesis_config.rent)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -236,9 +238,34 @@ mod tests {
|
||||
2 + 1,
|
||||
);
|
||||
|
||||
// exactly reserve as a remainder
|
||||
// exactly reserve as a remainder, reserve gets folded in
|
||||
let granularity = reserve * 3;
|
||||
let total_lamports = reserve + (granularity + reserve) * 2;
|
||||
create_and_check_stakes(
|
||||
&mut GenesisConfig {
|
||||
rent,
|
||||
..GenesisConfig::default()
|
||||
},
|
||||
&StakerInfo {
|
||||
name: "fun",
|
||||
staker: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
|
||||
withdrawer: "cafebabedeadbeef000000000000000000000000000000000000000000000000",
|
||||
sol: lamports_to_sol(total_lamports),
|
||||
custodian: "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
},
|
||||
&UnlockInfo {
|
||||
cliff_fraction: 0.5,
|
||||
cliff_years: 0.5,
|
||||
unlocks: 1,
|
||||
unlock_years: 0.5,
|
||||
},
|
||||
total_lamports,
|
||||
granularity,
|
||||
2 + 1,
|
||||
);
|
||||
// exactly reserve + 1 as a remainder, reserve + 1 gets its own stake
|
||||
let granularity = reserve * 3;
|
||||
let total_lamports = reserve + (granularity + reserve + 1) * 2;
|
||||
create_and_check_stakes(
|
||||
&mut GenesisConfig {
|
||||
rent,
|
||||
|
Reference in New Issue
Block a user