diff --git a/genesis/src/genesis_accounts.rs b/genesis/src/genesis_accounts.rs index 2e21055ec..919f2a850 100644 --- a/genesis/src/genesis_accounts.rs +++ b/genesis/src/genesis_accounts.rs @@ -650,15 +650,5 @@ mod tests { .sum::(); 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); } } diff --git a/genesis/src/main.rs b/genesis/src/main.rs index d6c85f4c6..633f57124 100644 --- a/genesis/src/main.rs +++ b/genesis/src/main.rs @@ -27,7 +27,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, @@ -342,7 +350,7 @@ fn main() -> Result<(), Box> { bootstrap_leader_stake_lamports, ); - let mut accounts = vec![ + let mut accounts: BTreeMap = [ // node needs an account to issue votes from ( bootstrap_leader_pubkey, @@ -352,13 +360,16 @@ fn main() -> Result<(), Box> { (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); @@ -537,27 +548,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 @@ -610,54 +622,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(); @@ -712,89 +707,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), ); }); } diff --git a/genesis/src/stakes.rs b/genesis/src/stakes.rs index 73841ccc8..bcbec1e2a 100644 --- a/genesis/src/stakes.rs +++ b/genesis/src/stakes.rs @@ -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, diff --git a/local-cluster/src/local_cluster.rs b/local-cluster/src/local_cluster.rs index dd2c7e907..190688e16 100644 --- a/local-cluster/src/local_cluster.rs +++ b/local-cluster/src/local_cluster.rs @@ -151,7 +151,10 @@ impl LocalCluster { match genesis_config.operating_mode { OperatingMode::SoftLaunch => { genesis_config.native_instruction_processors = - solana_genesis_programs::get_programs(genesis_config.operating_mode, 0).unwrap() + solana_genesis_programs::get_programs(genesis_config.operating_mode, 0) + .unwrap() + .into_iter() + .collect() } // create_genesis_config_with_leader() assumes OperatingMode::Development so do // nothing... @@ -169,18 +172,13 @@ impl LocalCluster { .push(solana_storage_program!()); let storage_keypair = Keypair::new(); - genesis_config.accounts.push(( + genesis_config.add_account( storage_keypair.pubkey(), storage_contract::create_validator_storage_account(leader_pubkey, 1), - )); + ); // Replace staking config - genesis_config.accounts = genesis_config - .accounts - .into_iter() - .filter(|(pubkey, _)| *pubkey != stake_config::id()) - .collect(); - genesis_config.accounts.push(( + genesis_config.add_account( stake_config::id(), stake_config::create_account( 1, @@ -189,7 +187,7 @@ impl LocalCluster { slash_penalty: std::u8::MAX, }, ), - )); + ); let (leader_ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_config); let leader_contact_info = leader_node.info.clone(); diff --git a/programs/storage/src/rewards_pools.rs b/programs/storage/src/rewards_pools.rs index ca7a93803..313a60e6a 100644 --- a/programs/storage/src/rewards_pools.rs +++ b/programs/storage/src/rewards_pools.rs @@ -45,12 +45,7 @@ mod tests { add_genesis_accounts(&mut genesis_config); for _i in 0..NUM_REWARDS_POOLS { - let id = random_id(); - assert!(genesis_config - .rewards_pools - .iter() - .position(|x| x.0 == id) - .is_some()); + assert!(genesis_config.rewards_pools.get(&random_id()).is_some()) } } } diff --git a/runtime/src/genesis_utils.rs b/runtime/src/genesis_utils.rs index 59aadc0bf..d3312ba61 100644 --- a/runtime/src/genesis_utils.rs +++ b/runtime/src/genesis_utils.rs @@ -49,7 +49,7 @@ pub fn create_genesis_config_with_leader( bootstrap_leader_stake_lamports, ); - let accounts = vec![ + let accounts = [ ( mint_keypair.pubkey(), Account::new(mint_lamports, 0, &system_program::id()), @@ -66,7 +66,10 @@ pub fn create_genesis_config_with_leader( bootstrap_leader_staking_keypair.pubkey(), bootstrap_leader_stake_account, ), - ]; + ] + .iter() + .cloned() + .collect(); // Bare minimum program set let native_instruction_processors = vec![ diff --git a/sdk/src/genesis_config.rs b/sdk/src/genesis_config.rs index cabb57452..b2a9e1727 100644 --- a/sdk/src/genesis_config.rs +++ b/sdk/src/genesis_config.rs @@ -16,6 +16,7 @@ use crate::{ use bincode::{deserialize, serialize}; use memmap::Mmap; use std::{ + collections::BTreeMap, fs::{File, OpenOptions}, io::Write, path::{Path, PathBuf}, @@ -29,9 +30,9 @@ pub enum OperatingMode { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct GenesisConfig { - pub accounts: Vec<(Pubkey, Account)>, + pub accounts: BTreeMap, pub native_instruction_processors: Vec<(String, Pubkey)>, - pub rewards_pools: Vec<(Pubkey, Account)>, + pub rewards_pools: BTreeMap, pub ticks_per_slot: u64, pub slots_per_segment: u64, pub poh_config: PohConfig, @@ -60,9 +61,9 @@ pub fn create_genesis_config(lamports: u64) -> (GenesisConfig, Keypair) { impl Default for GenesisConfig { fn default() -> Self { Self { - accounts: Vec::new(), - native_instruction_processors: Vec::new(), - rewards_pools: Vec::new(), + accounts: BTreeMap::default(), + native_instruction_processors: Vec::default(), + rewards_pools: BTreeMap::default(), ticks_per_slot: DEFAULT_TICKS_PER_SLOT, slots_per_segment: DEFAULT_SLOTS_PER_SEGMENT, poh_config: PohConfig::default(), @@ -81,15 +82,18 @@ impl GenesisConfig { native_instruction_processors: &[(String, Pubkey)], ) -> Self { Self { - accounts: accounts.to_vec(), + accounts: accounts + .iter() + .cloned() + .collect::>(), native_instruction_processors: native_instruction_processors.to_vec(), ..GenesisConfig::default() } } pub fn hash(&self) -> Hash { - let serialized = serde_json::to_string(self).unwrap(); - hash(&serialized.into_bytes()) + let serialized = serialize(&self).unwrap(); + hash(&serialized) } fn genesis_filename(ledger_path: &Path) -> PathBuf { @@ -140,7 +144,7 @@ impl GenesisConfig { } pub fn add_account(&mut self, pubkey: Pubkey, account: Account) { - self.accounts.push((pubkey, account)); + self.accounts.insert(pubkey, account); } pub fn add_native_instruction_processor(&mut self, name: String, program_id: Pubkey) { @@ -148,7 +152,7 @@ impl GenesisConfig { } pub fn add_rewards_pool(&mut self, pubkey: Pubkey, account: Account) { - self.rewards_pools.push((pubkey, account)); + self.rewards_pools.insert(pubkey, account); } }