diff --git a/accounts-db/src/accounts_db.rs b/accounts-db/src/accounts_db.rs index b4a74f83568116..fcbec8c73c17d3 100644 --- a/accounts-db/src/accounts_db.rs +++ b/accounts-db/src/accounts_db.rs @@ -7725,7 +7725,6 @@ impl AccountsDb { info!("calculate_accounts_hash_from_storages: slot: {slot}, {accounts_hash:?}, capitalization: {capitalization}"); Ok((accounts_hash, capitalization)) }; - let result = if use_bg_thread_pool { self.thread_pool_clean.install(scan_and_hash) } else { diff --git a/core/src/accounts_hash_verifier.rs b/core/src/accounts_hash_verifier.rs index 39849253f94527..d3e4ab40907ffa 100644 --- a/core/src/accounts_hash_verifier.rs +++ b/core/src/accounts_hash_verifier.rs @@ -438,7 +438,17 @@ impl AccountsHashVerifier { ) .unwrap()); // unwrap here will never fail since check_hash = false + trace!( + "compute accounts_package.expected_capitalization and lamports {} {} for slot {}", + accounts_package.expected_capitalization, + lamports, + slot + ); + if accounts_package.expected_capitalization != lamports { + trace!( + "accounts_package.expected_capitalization need recalc as values are not the same" + ); // before we assert, run the hash calc again. This helps track down whether it could have been a failure in a race condition possibly with shrink. // We could add diagnostics to the hash calc here to produce a per bin cap or something to help narrow down how many pubkeys are different. let calculate_accounts_hash_config = CalcAccountsHashConfig { @@ -464,12 +474,17 @@ impl AccountsHashVerifier { &sorted_storages, HashStats::default(), ); + trace!( + "after recalc compute accounts_package.expected_capitalization and lamports {} {} for slot {}", + accounts_package.expected_capitalization, lamports, slot + ); } - assert_eq!( - accounts_package.expected_capitalization, lamports, - "accounts hash capitalization mismatch" - ); + // TODO: re-enable this assert once we have a better understanding of the issue and repair it. + // assert_eq!( + // accounts_package.expected_capitalization, lamports, + // "accounts hash capitalization mismatch" + // ); if let Some(expected_hash) = accounts_package.accounts_hash_for_testing { assert_eq!(expected_hash, accounts_hash); }; diff --git a/genesis/src/genesis_accounts.rs b/genesis/src/genesis_accounts.rs deleted file mode 100644 index 0704985913838f..00000000000000 --- a/genesis/src/genesis_accounts.rs +++ /dev/null @@ -1,285 +0,0 @@ -use { - crate::{ - stakes::{create_and_add_stakes, StakerInfo}, - unlocks::UnlockInfo, - }, - solana_sdk::{genesis_config::GenesisConfig, native_token::LAMPORTS_PER_SOL}, -}; - -// 9 month schedule is 100% after 9 months -const UNLOCKS_ALL_AT_9_MONTHS: UnlockInfo = UnlockInfo { - cliff_fraction: 1.0, - cliff_years: 0.75, - unlocks: 0, - unlock_years: 0.0, - custodian: "Mc5XB47H3DKJHym5RLa9mPzWv5snERsF3KNv5AauXK8", -}; - -// 9 month schedule is 50% after 9 months, then monthly for 2 years -const UNLOCKS_HALF_AT_9_MONTHS: UnlockInfo = UnlockInfo { - cliff_fraction: 0.5, - cliff_years: 0.75, - unlocks: 24, - unlock_years: 2.0, - custodian: "Mc5XB47H3DKJHym5RLa9mPzWv5snERsF3KNv5AauXK8", -}; - -// no lockups -const UNLOCKS_ALL_DAY_ZERO: UnlockInfo = UnlockInfo { - cliff_fraction: 1.0, - cliff_years: 0.0, - unlocks: 0, - unlock_years: 0.0, - custodian: "Mc5XB47H3DKJHym5RLa9mPzWv5snERsF3KNv5AauXK8", -}; - -pub const CREATOR_STAKER_INFOS: &[StakerInfo] = &[ - StakerInfo { - name: "impossible pizza", - staker: "uE3TVEffRp69mrgknYr71M18GDqL7GxCNGYYRjb3oUt", - lamports: 5_000_000 * LAMPORTS_PER_SOL, - withdrawer: Some("59SLqk4ete5QttM1WmjfMA7uNJnJVFLQqXJSy9rvuj7c"), - }, - StakerInfo { - name: "nutritious examination", - staker: "9noVEZreMmgQvE8iyKmxy7CGTJ2enELyuJ1qxFtXrfJB", - lamports: 5_000_000 * LAMPORTS_PER_SOL, - withdrawer: Some("ERnx3Csgu3LjrGGrCeCUZzuHguRu6XabT1kufSB1NDWi"), - }, - StakerInfo { - name: "tidy impression", - staker: "BU7LA4kYvicfPCp22EM2Tth3eaeWAXYo6yCgWXQFJ42z", - lamports: 5_000_000 * LAMPORTS_PER_SOL, - withdrawer: Some("5eKcGy7ZCPJdQSQGVnfmT7kGz6MKPMKaNaMEYJbmwhuT"), - }, - StakerInfo { - name: "dramatic treatment", - staker: "BrNFrFeuev8TosKhRe2kvVZTYrcUuYaqCfptWutxs17B", - lamports: 1_205_602 * LAMPORTS_PER_SOL, - withdrawer: Some("2pKqwFKfKj2nGrknPNDSP8vXGYrgAjd28fT6yLew8sT3"), - }, - StakerInfo { - name: "angry noise", - staker: "34HCVh8Yx4jNkaeLUQEKibFKUZDPQMjWzkXy8qUfdhS4", - lamports: 5_000_000 * LAMPORTS_PER_SOL, - withdrawer: Some("Hw3sP6PreBtFCnwXbNvUypMhty62GXibjfiZ1zHBXFk6"), - }, - StakerInfo { - name: "hard cousin", - staker: "AyZb3xrZE8wnS6gYBdsJg5v8CjyrX2ZGXU2zMakCFyYd", - lamports: 5_000_000 * LAMPORTS_PER_SOL, - withdrawer: Some("9j3WzBSZRHrD2DbzFTUVVi81QX6boVvUTpGWcSiMwD5W"), - }, - StakerInfo { - name: "lopsided skill", - staker: "7SbpY8LmZUb5XRqDbyoreUrSVVV9c39wkpEz81kEAXu5", - lamports: 5_000_000 * LAMPORTS_PER_SOL, - withdrawer: Some("EJyZGbQ1PmpcWxfqGME6SUNHfurh1zggDqCT7rV9xLzL"), - }, - StakerInfo { - name: "red snake", - staker: "C9CfFpmLDsQsz6wt7MrrZquNB5oS4QkpJkmDAiboVEZZ", - lamports: 3_655_292 * LAMPORTS_PER_SOL, - withdrawer: Some("JBGnGdLyo7V2z9hz51mnnbyDp9sBACtw5WYH9YRG8n7e"), - }, - StakerInfo { - name: "jolly year", - staker: "5WbxKiW7bghkr8JN6ZAv2TQt4PfJFvtuqNaN8gyQ5UzU", - lamports: 5_000_000 * LAMPORTS_PER_SOL, - withdrawer: Some("43XAfG3AFiF1ockdh7xp91fpFyZkbWSZq9ZFBCGUVV41"), - }, - StakerInfo { - name: "typical initiative", - staker: "Gc8XnHU6Nnriwt9RbEwi7PTosx4YanLyXak9GTbB8VaH", - lamports: 5_000_000 * LAMPORTS_PER_SOL, - withdrawer: Some("7s2GVwFo8VSrCwX9Tztt42ueiEaUtJ6zCEHU8XGvuf5E"), - }, - StakerInfo { - name: "deserted window", - staker: "AMmYEynkd78uNTZDFFrMw6NKjWTgqW7M8EFjvajk23VR", - lamports: 3_655_292 * LAMPORTS_PER_SOL, - withdrawer: Some("23PJYLS1WFLqhXnXq2Hobc17DbvZaoinoTZYLyGRT8E2"), - }, - StakerInfo { - name: "eight nation", - staker: "4qWoqt71p7h6siSDS6osu4oVWpw8R7E6uYYiY7Z6oJbH", - lamports: 103_519 * LAMPORTS_PER_SOL, - withdrawer: Some("6bFjx3egMjVsGKFb445564a4bwgibwbUB2tVFsJcdPv7"), - }, - StakerInfo { - name: "earsplitting meaning", - staker: "GYitoBY53E9awc56NWHJ2kxMwj4do5GSmvTRowjGaRDw", - lamports: 5_000_000 * LAMPORTS_PER_SOL, - withdrawer: Some("jXMEkVQQpoqebVMGN7DfpvdRLwJDEkoVNrwPVphNm7i"), - }, - StakerInfo { - name: "alike cheese", - staker: "Drg9uSvSEfjtn15jqmmrEQnA4pvU1ToYSGSa1Dv9C6Fk", - lamports: 3_880_295 * LAMPORTS_PER_SOL, - withdrawer: Some("BxmwgfnyAqZnqRCJGdsEea35pcc92GFTcyGeSj4RNfJJ"), - }, - StakerInfo { - name: "noisy honey", - staker: "95HsPFFvwbWpk42MKzenauSoULNzk8Tg6fc6EiJhLsUZ", - lamports: 5_000_000 * LAMPORTS_PER_SOL, - withdrawer: Some("Aj3K933zdRQhYEJi2Yjz8hJWXN3Z3hrKJQtPtE8VmUnq"), - }, -]; - -pub const SERVICE_STAKER_INFOS: &[StakerInfo] = &[ - StakerInfo { - name: "wretched texture", - staker: "B1hegzthtfNQxyEPzkESySxRjMidNqaxrzbQ28GaEwn8", - lamports: 225_000 * LAMPORTS_PER_SOL, - withdrawer: Some("HWzeqw1Yk5uiLgT2uGUim5ocFJNCwYUFbeCtDVpx9yUb"), - }, - StakerInfo { - name: "unbecoming silver", - staker: "4AcoZa1P8fF5XK21RJsiuMRZPEScbbWNc75oakRFHiBz", - lamports: 28_800 * LAMPORTS_PER_SOL, - withdrawer: None, - }, - StakerInfo { - name: "inexpensive uncle", - staker: "AkJ7yssRqS3X4UWLUsPTxbP6LfVgdPYBWH4Jgk5EETgZ", - lamports: 300_000 * LAMPORTS_PER_SOL, - withdrawer: Some("6mudxxoe5VyXXNXsJ3NSGSTGESfG2t86PBCQGbouHpXX"), - }, - StakerInfo { - name: "hellish money", - staker: "4DVkqvRP8y26JvzNwsnQEQuC7HASwpGs58GsAT9XJMVg", - lamports: 200_000 * LAMPORTS_PER_SOL, - withdrawer: Some("ASJpWZAxY96kbciLqzb7sg45gsH32yPzGcxjn7HPcARn"), - }, - StakerInfo { - name: "full grape", - staker: "B2EWnwgmNd3KMpD71yZMijhML1jd4TYp96zJdhMiWZ7b", - lamports: 450_000 * LAMPORTS_PER_SOL, - withdrawer: Some("9oaCkokBBhgBsgyg4sL7fMJyQseaJb1TbADZeoPdpWdc"), - }, - StakerInfo { - name: "nice ghost", - staker: "HtQS1CH3nsUHmnLpenj5W6KHzFWTf3mzCn1mTqK7LkB7", - lamports: 650_000 * LAMPORTS_PER_SOL, - withdrawer: Some("4YnNnycEZXCkuVs2hDthdNxMD4E8wc7ZPgyAK7Lm1uZc"), - }, -]; - -pub const FOUNDATION_STAKER_INFOS: &[StakerInfo] = &[ - StakerInfo { - name: "lyrical supermarket", - staker: "4xh7vtQCTim3vgpQ1dQQWjtKrBSkbtL3s15FimXVJAAP", - lamports: 5_000_000 * LAMPORTS_PER_SOL, - withdrawer: Some("C7WS9ic7KN9XNcLsNoMvzTvbzURM3rFGDEQN7qJMWNLn"), - }, - StakerInfo { - name: "frequent description", - staker: "95Nf8XfoecteSXU9nbcvzkrFQdu6FqPaH3EvhwLaC83t", - lamports: 57_500_000 * LAMPORTS_PER_SOL, - withdrawer: Some("FdGYQdiRky8NZzN9wZtczTBcWLYYRXrJ3LMDhqDPn5rM"), - }, -]; - -pub const GRANTS_STAKER_INFOS: &[StakerInfo] = &[ - StakerInfo { - name: "rightful agreement", - staker: "8w5cgUQfXAZZWyVgenPHpQ1uABXUVLnymqXbuZPx7yqt", - lamports: 5_000_000 * LAMPORTS_PER_SOL, - withdrawer: Some("EDwSQShtUWQtmFfN9SpUUd6hgonL7tRdxngAsNKv9Pe6"), - }, - StakerInfo { - name: "tasty location", - staker: "9eyXtP43dCp59oyvWG2R7WQCeJ2bA6TWoLzXg1KTDfQQ", - lamports: 15_000_000 * LAMPORTS_PER_SOL, - withdrawer: Some("9BgvWHerNACjnx6ZpK51k2LEsnwBP3gFwWDzhKkHKH1m"), - }, -]; - -pub const COMMUNITY_STAKER_INFOS: &[StakerInfo] = &[ - StakerInfo { - name: "shrill charity", - staker: "Eo1iDtrZZiAkQFA8u431hedChaSUnPbU8MWg849MFvEZ", - lamports: 5_000_000 * LAMPORTS_PER_SOL, - withdrawer: Some("8CUUMKYNGxdgYio5CLHRHyzMEhhVRMcqefgE6dLqnVRK"), - }, - StakerInfo { - name: "legal gate", - staker: "7KCzZCbZz6V1U1YXUpBNaqQzQCg2DKo8JsNhKASKtYxe", - lamports: 30_301_032 * LAMPORTS_PER_SOL, - withdrawer: Some("92viKFftk1dJjqJwreFqT2qHXxjSUuEE9VyHvTdY1mpY"), - }, - StakerInfo { - name: "cluttered complaint", - staker: "2J8mJU6tWg78DdQVEqMfpN3rMeNbcRT9qGL3yLbmSXYL", - lamports: 153_333_633 * LAMPORTS_PER_SOL + 41 * LAMPORTS_PER_SOL / 100, - withdrawer: Some("7kgfDmgbEfypBujqn4tyApjf8H7ZWuaL3F6Ah9vQHzgR"), - }, -]; - -fn add_stakes( - genesis_config: &mut GenesisConfig, - staker_infos: &[StakerInfo], - unlock_info: &UnlockInfo, -) -> u64 { - staker_infos - .iter() - .map(|staker_info| create_and_add_stakes(genesis_config, staker_info, unlock_info, None)) - .sum::() -} - -pub fn add_genesis_accounts(genesis_config: &mut GenesisConfig, mut issued_lamports: u64) { - // add_stakes() and add_validators() award tokens for rent exemption and - // to cover an initial transfer-free period of the network - - issued_lamports += add_stakes( - genesis_config, - CREATOR_STAKER_INFOS, - &UNLOCKS_HALF_AT_9_MONTHS, - ) + add_stakes( - genesis_config, - SERVICE_STAKER_INFOS, - &UNLOCKS_ALL_AT_9_MONTHS, - ) + add_stakes( - genesis_config, - FOUNDATION_STAKER_INFOS, - &UNLOCKS_ALL_DAY_ZERO, - ) + add_stakes(genesis_config, GRANTS_STAKER_INFOS, &UNLOCKS_ALL_DAY_ZERO) - + add_stakes( - genesis_config, - COMMUNITY_STAKER_INFOS, - &UNLOCKS_ALL_DAY_ZERO, - ); - - // "one thanks" (community pool) gets 500_000_000SOL (total) - above distributions - create_and_add_stakes( - genesis_config, - &StakerInfo { - name: "one thanks", - staker: "7vEAL3nS9CWmy1q6njUUyHE7Cf5RmyQpND6CsoHjzPiR", - lamports: (500_000_000 * LAMPORTS_PER_SOL).saturating_sub(issued_lamports), - withdrawer: Some("3FFaheyqtyAXZSYxDzsr5CVKvJuvZD1WE1VEsBtDbRqB"), - }, - &UNLOCKS_ALL_DAY_ZERO, - None, - ); -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_add_genesis_accounts() { - let mut genesis_config = GenesisConfig::default(); - - add_genesis_accounts(&mut genesis_config, 0); - - let lamports = genesis_config - .accounts - .values() - .map(|account| account.lamports) - .sum::(); - - assert_eq!(500_000_000 * LAMPORTS_PER_SOL, lamports); - } -} diff --git a/genesis/src/lib.rs b/genesis/src/lib.rs index 5faf788d0f4f8a..b2f6bb4576d633 100644 --- a/genesis/src/lib.rs +++ b/genesis/src/lib.rs @@ -1,6 +1,5 @@ #![allow(clippy::arithmetic_side_effects)] pub mod address_generator; -pub mod genesis_accounts; pub mod stakes; pub mod unlocks; diff --git a/genesis/src/main.rs b/genesis/src/main.rs index 6b7efd5e664339..03bfe68f04a31d 100644 --- a/genesis/src/main.rs +++ b/genesis/src/main.rs @@ -15,7 +15,7 @@ use { }, }, solana_entry::poh::compute_hashes_per_tick, - solana_genesis::{genesis_accounts::add_genesis_accounts, Base64Account}, + solana_genesis::Base64Account, solana_ledger::{blockstore::create_new_ledger, blockstore_options::LedgerColumnOptions}, solana_sdk::{ account::{Account, AccountSharedData, ReadableAccount, WritableAccount}, @@ -577,7 +577,6 @@ fn main() -> Result<(), Box> { ); } - solana_stake_program::add_genesis_accounts(&mut genesis_config); if genesis_config.cluster_type == ClusterType::Development { solana_runtime::genesis_utils::activate_all_features(&mut genesis_config); } @@ -591,14 +590,6 @@ fn main() -> Result<(), Box> { let max_genesis_archive_unpacked_size = value_t_or_exit!(matches, "max_genesis_archive_unpacked_size", u64); - let issued_lamports = genesis_config - .accounts - .values() - .map(|account| account.lamports) - .sum::(); - - add_genesis_accounts(&mut genesis_config, issued_lamports - faucet_lamports); - let parse_address = |address: &str, input_type: &str| { address.parse::().unwrap_or_else(|err| { eprintln!("Error: invalid {input_type} {address}: {err}"); diff --git a/genesis/src/stakes.rs b/genesis/src/stakes.rs index b1f545b218fa6f..bd2fcd708be19c 100644 --- a/genesis/src/stakes.rs +++ b/genesis/src/stakes.rs @@ -1,23 +1,4 @@ //! stakes generator -use { - crate::{ - address_generator::AddressGenerator, - unlocks::{UnlockInfo, Unlocks}, - }, - solana_sdk::{ - account::Account, - clock::Slot, - genesis_config::GenesisConfig, - pubkey::Pubkey, - stake::{ - self, - state::{Authorized, Lockup, StakeStateV2}, - }, - system_program, - timing::years_as_slots, - }, - solana_stake_program::stake_state::create_lockup_stake_account, -}; #[derive(Debug)] pub struct StakerInfo { @@ -26,326 +7,3 @@ pub struct StakerInfo { pub withdrawer: Option<&'static str>, pub lamports: u64, } - -// lamports required to run staking operations for one year -// 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_fees(genesis_config: &GenesisConfig, years: f64) -> u64 { - genesis_config.fee_rate_governor.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 -/// account -pub fn create_and_add_stakes( - genesis_config: &mut GenesisConfig, - // information about this staker for this group of stakes - staker_info: &StakerInfo, - // description of how the stakes' lockups will expire - unlock_info: &UnlockInfo, - // the largest each stake account should be, in lamports - granularity: Option, -) -> u64 { - let granularity = granularity.unwrap_or(std::u64::MAX); - let staker = &staker_info - .staker - .parse::() - .expect("invalid staker"); - let withdrawer = &staker_info - .withdrawer - .unwrap_or(staker_info.staker) - .parse::() - .expect("invalid staker"); - let authorized = Authorized { - staker: *staker, - withdrawer: *withdrawer, - }; - let custodian = unlock_info - .custodian - .parse::() - .expect("invalid custodian"); - - let total_lamports = staker_info.lamports; - - // staker is a system account - let staker_rent_reserve = genesis_config.rent.minimum_balance(0).max(1); - let staker_fees = calculate_staker_fees(genesis_config, 1.0); - - let mut stakes_lamports = total_lamports - staker_fees; - - // 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 - // a single staker may administer any number of accounts - 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 - // as we support one re-delegation per epoch - let unlocks = Unlocks::new( - unlock_info.cliff_fraction, - unlock_info.cliff_years, - unlock_info.unlocks, - unlock_info.unlock_years, - &genesis_config.epoch_schedule, - &genesis_config.poh_config.target_tick_duration, - genesis_config.ticks_per_slot, - ); - - let mut address_generator = AddressGenerator::new(&authorized.staker, &stake::program::id()); - - let stake_rent_reserve = genesis_config.rent.minimum_balance(StakeStateV2::size_of()); - - for unlock in unlocks { - let lamports = unlock.amount(stakes_lamports); - - let (granularity, remainder) = if granularity < lamports { - (granularity, lamports % granularity) - } else { - (lamports, 0) - }; - - let lockup = Lockup { - epoch: unlock.epoch, - custodian, - unix_timestamp: 0, - }; - for _ in 0..(lamports / granularity).saturating_sub(1) { - genesis_config.add_account( - address_generator.next(), - create_lockup_stake_account( - &authorized, - &lockup, - &genesis_config.rent, - granularity, - ), - ); - } - if remainder <= stake_rent_reserve { - genesis_config.add_account( - address_generator.next(), - create_lockup_stake_account( - &authorized, - &lockup, - &genesis_config.rent, - granularity + remainder, - ), - ); - } else { - genesis_config.add_account( - address_generator.next(), - create_lockup_stake_account( - &authorized, - &lockup, - &genesis_config.rent, - granularity, - ), - ); - genesis_config.add_account( - address_generator.next(), - create_lockup_stake_account(&authorized, &lockup, &genesis_config.rent, remainder), - ); - } - } - total_lamports -} - -#[cfg(test)] -mod tests { - use {super::*, solana_sdk::rent::Rent}; - - fn create_and_check_stakes( - genesis_config: &mut GenesisConfig, - staker_info: &StakerInfo, - unlock_info: &UnlockInfo, - total_lamports: u64, - granularity: u64, - len: usize, - ) { - assert_eq!( - total_lamports, - create_and_add_stakes(genesis_config, staker_info, unlock_info, Some(granularity)) - ); - assert_eq!(genesis_config.accounts.len(), len); - assert_eq!( - genesis_config - .accounts - .values() - .map(|account| account.lamports) - .sum::(), - total_lamports, - ); - assert!(genesis_config - .accounts - .iter() - .all(|(_pubkey, account)| account.lamports <= granularity - || account.lamports - granularity - <= genesis_config.rent.minimum_balance(StakeStateV2::size_of()))); - } - - // #[ignore] - // #[test] - // fn hex_test_keys_to_bs58() { - // vec![ - // "ab22196afde08a090a3721eb20e3e1ea84d36e14d1a3f0815b236b300d9d33ef", // CX2sgoat51bnDgCN2YeesrTcscgVhnhWnwxtWEEEqBs4 - // "a2a7ae9098f862f4b3ba7d102d174de5e84a560444c39c035f3eeecce442eadc", // BwwM47pLHwUgjJXKQKVNiRfGhtPNWfNLH27na2HJQHhd - // "6a56514c29f6b1de4d46164621d6bd25b337a711f569f9283c1143c7e8fb546e", // 8A6ZEEW2odkqXNjTWHNG6tUk7uj6zCzHueTyEr9pM1tH - // "b420af728f58d9f269d6e07fbbaecf6ed6535e5348538e3f39f2710351f2b940", // D89HyaBmr2WmrTehsfkQrY23wCXcDfsFnN9gMfUXHaDd - // "ddf2e4c81eafae2d68ac99171b066c87bddb168d6b7c07333cd951f36640163d", // FwPvDpvUmnco1CSfwXQDTbUbuhG5eP7h2vgCKYKVL7at - // "312fa06ccf1b671b26404a34136161ed2aba9e66f248441b4fddb5c592fde560", // 4K16iBoC9kAQRT8pUEKeD2h9WEx1zsRgEmJFssXcXmqq - // "0cbf98cd35ceff84ca72b752c32cc3eeee4f765ca1bef1140927ebf5c6e74339", // rmLpENW4V6QNeEhdJJVxo9Xt99oKgNUFZS4Y4375amW - // "467e06fa25a9e06824eedc926ce431947ed99c728bed36be54561354c1330959", // 5kAztE3XtrpeyGZZxckSUt3ZWojNTmph1QSC9S2682z4 - // "ef1562bf9edfd0f5e62530cce4244e8de544a3a30075a2cd5c9074edfbcbe78a", // H6HMVuDR8XCw3EuhLvFG4EciVvGo76Agq1kSBL2ozoDs - // "2ab26abb9d8131a30a4a63446125cf961ece4b926c31cce0eb84da4eac3f836e", // 3sfv8tk5ZSDBWbTkFkvFxCvJUyW5yDJUu6VMJcUARQWq - // ] - // .iter() - // .for_each(|_hex| { - // print( - // "\n\"{}\", // {:?}", - // hex, - // Pubkey::try_from(&hex::decode(hex).unwrap()).unwrap() - // ); - // }); - // println(); - // println( - // "{:?}", - // "P1aceHo1derPubkey11111111111111111111111111" - // .parse::() - // .unwrap() - // ); - //} - - #[test] - fn test_create_stakes() { - // 2 unlocks - - let rent = Rent { - lamports_per_byte_year: 1, - exemption_threshold: 1.0, - ..Rent::default() - }; - - let reserve = rent.minimum_balance(StakeStateV2::size_of()); - let staker_reserve = rent.minimum_balance(0); - - // verify that a small remainder ends up in the last stake - let granularity = reserve; - let total_lamports = staker_reserve + reserve * 2 + 1; - create_and_check_stakes( - &mut GenesisConfig { - #[allow(clippy::clone_on_copy)] - rent: rent.clone(), - ..GenesisConfig::default() - }, - &StakerInfo { - name: "fun", - staker: "P1aceHo1derPubkey11111111111111111111111111", - lamports: total_lamports, - withdrawer: None, - }, - &UnlockInfo { - cliff_fraction: 0.5, - cliff_years: 0.5, - unlocks: 1, - unlock_years: 0.5, - custodian: "11111111111111111111111111111111", - }, - total_lamports, - granularity, - 2 + 1, - ); - - // huge granularity doesn't blow up - let granularity = std::u64::MAX; - let total_lamports = staker_reserve + reserve * 2 + 1; - create_and_check_stakes( - &mut GenesisConfig { - #[allow(clippy::clone_on_copy)] - rent: rent.clone(), - ..GenesisConfig::default() - }, - &StakerInfo { - name: "fun", - staker: "P1aceHo1derPubkey11111111111111111111111111", - lamports: total_lamports, - withdrawer: None, - }, - &UnlockInfo { - cliff_fraction: 0.5, - cliff_years: 0.5, - unlocks: 1, - unlock_years: 0.5, - custodian: "11111111111111111111111111111111", - }, - total_lamports, - granularity, - 2 + 1, - ); - - // exactly reserve as a remainder, reserve gets folded in - let granularity = reserve * 3; - let total_lamports = staker_reserve + (granularity + reserve) * 2; - create_and_check_stakes( - &mut GenesisConfig { - #[allow(clippy::clone_on_copy)] - rent: rent.clone(), - ..GenesisConfig::default() - }, - &StakerInfo { - name: "fun", - staker: "P1aceHo1derPubkey11111111111111111111111111", - lamports: total_lamports, - withdrawer: None, - }, - &UnlockInfo { - cliff_fraction: 0.5, - cliff_years: 0.5, - unlocks: 1, - unlock_years: 0.5, - custodian: "11111111111111111111111111111111", - }, - total_lamports, - granularity, - 2 + 1, - ); - // exactly reserve + 1 as a remainder, reserve + 1 gets its own stake - let granularity = reserve * 3; - let total_lamports = staker_reserve + (granularity + reserve + 1) * 2; - create_and_check_stakes( - &mut GenesisConfig { - #[allow(clippy::clone_on_copy)] - rent: rent.clone(), - ..GenesisConfig::default() - }, - &StakerInfo { - name: "fun", - staker: "P1aceHo1derPubkey11111111111111111111111111", - lamports: total_lamports, - withdrawer: None, - }, - &UnlockInfo { - cliff_fraction: 0.5, - cliff_years: 0.5, - unlocks: 1, - unlock_years: 0.5, - custodian: "11111111111111111111111111111111", - }, - total_lamports, - granularity, - 4 + 1, - ); - } -} diff --git a/multinode-demo/setup.sh b/multinode-demo/setup.sh index 306c246ad9fabe..00cd98d3a35def 100755 --- a/multinode-demo/setup.sh +++ b/multinode-demo/setup.sh @@ -36,6 +36,7 @@ args=( "$@" --max-genesis-archive-unpacked-size 1073741824 --enable-warmup-epochs + --slots-per-epoch 1000 --bootstrap-validator "$SOLANA_CONFIG_DIR"/bootstrap-validator/identity.json "$SOLANA_CONFIG_DIR"/bootstrap-validator/vote-account.json "$SOLANA_CONFIG_DIR"/bootstrap-validator/stake-account.json @@ -52,7 +53,10 @@ fi default_arg --ledger "$SOLANA_CONFIG_DIR"/bootstrap-validator default_arg --faucet-pubkey "$SOLANA_CONFIG_DIR"/faucet.json default_arg --faucet-lamports 500000000000000000 +default_arg --inflation none default_arg --hashes-per-tick auto +default_arg --target-lamports-per-signature 100 +default_arg --target-signatures-per-slot 5 default_arg --cluster-type development $solana_genesis "${args[@]}" diff --git a/sdk/src/fee.rs b/sdk/src/fee.rs index f3377b5254f0a6..db5ac4975745a2 100644 --- a/sdk/src/fee.rs +++ b/sdk/src/fee.rs @@ -1,8 +1,100 @@ //! Fee structures. +use std::collections::{HashMap}; +use std::str::FromStr; + +use lazy_static::lazy_static; use crate::native_token::sol_to_lamports; +use log::trace; +use solana_program::borsh1::try_from_slice_unchecked; + #[cfg(not(target_os = "solana"))] use solana_program::message::SanitizedMessage; +use solana_program::pubkey::Pubkey; +use crate::{compute_budget}; +use crate::compute_budget::ComputeBudgetInstruction; + +pub const COMPUTE_UNIT_TO_US_RATIO: u64 = 30; +pub const SIGNATURE_COST: u64 = COMPUTE_UNIT_TO_US_RATIO * 24; +pub const SECP256K1_VERIFY_COST: u64 = COMPUTE_UNIT_TO_US_RATIO * 223; +pub const ED25519_VERIFY_COST: u64 = COMPUTE_UNIT_TO_US_RATIO * 76; +pub const WRITE_LOCK_UNITS: u64 = COMPUTE_UNIT_TO_US_RATIO * 10; +pub const DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT: u32 = 200_000; +pub const MAX_COMPUTE_UNIT_LIMIT: u32 = 1_400_000; +pub const HEAP_LENGTH: usize = 32 * 1024; +pub const MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES: u32 = 64 * 1024 * 1024; + +lazy_static! { + pub static ref BUILT_IN_INSTRUCTION_COSTS: HashMap = [ + (Pubkey::from_str("Stake11111111111111111111111111111111111111").unwrap(), 750u64), + (Pubkey::from_str("Config1111111111111111111111111111111111111").unwrap(), 450u64), + (Pubkey::from_str("Vote111111111111111111111111111111111111111").unwrap(), 2_100u64), + (Pubkey::from_str("11111111111111111111111111111111").unwrap(), 150u64), + (Pubkey::from_str("ComputeBudget111111111111111111111111111111").unwrap(), 150u64), + (Pubkey::from_str("AddressLookupTab1e1111111111111111111111111").unwrap(), 750u64), + (Pubkey::from_str("BPFLoaderUpgradeab1e11111111111111111111111").unwrap(), 2_370u64), + (Pubkey::from_str("BPFLoader1111111111111111111111111111111111").unwrap(), 1_140u64), + (Pubkey::from_str("BPFLoader2111111111111111111111111111111111").unwrap(), 570u64), + (Pubkey::from_str("LoaderV411111111111111111111111111111111111").unwrap(), 2_000u64), + // Note: These are precompile, run directly in bank during sanitizing; + (Pubkey::from_str("KeccakSecp256k11111111111111111111111111111").unwrap(), 0u64), + (Pubkey::from_str("Ed25519SigVerify111111111111111111111111111").unwrap(), 0u64) + ] + .iter() + .cloned() + .collect(); +} + +fn get_compute_unit_price_from_message( + message: &SanitizedMessage, +) -> u64 { + let mut compute_unit_price = 0u64; + // Iterate through instructions and search for ComputeBudgetInstruction::SetComputeUnitPrice + for (program_id, instruction) in message.program_instructions_iter() { + if compute_budget::check_id(program_id) { + if let Ok(ComputeBudgetInstruction::SetComputeUnitPrice(price)) = + try_from_slice_unchecked(&instruction.data) + { + // Set the compute unit price in tx_cost + compute_unit_price = price; + } + } + } + compute_unit_price +} + +fn get_transaction_cost( + message: &SanitizedMessage, + budget_limits: &FeeBudgetLimits +) -> u64 { + let mut builtin_costs = 0u64; + let mut bpf_costs = 0u64; + let mut compute_unit_limit_is_set = false; + + for (program_id, instruction) in message.program_instructions_iter() { + // to keep the same behavior, look for builtin first + if let Some(builtin_cost) = BUILT_IN_INSTRUCTION_COSTS.get(program_id) { + builtin_costs = builtin_costs.saturating_add(*builtin_cost); + } else { + bpf_costs = bpf_costs + .saturating_add(u64::from(DEFAULT_INSTRUCTION_COMPUTE_UNIT_LIMIT)) + .min(u64::from(MAX_COMPUTE_UNIT_LIMIT)); + } + if compute_budget::check_id(program_id) { + if let Ok(ComputeBudgetInstruction::SetComputeUnitLimit(_)) = + try_from_slice_unchecked(&instruction.data) + { + compute_unit_limit_is_set = true; + } + } + } + + if bpf_costs > 0 && compute_unit_limit_is_set { + bpf_costs = budget_limits.compute_unit_limit + } + + builtin_costs.saturating_add(bpf_costs) +} /// A fee and its associated compute unit limit #[derive(Debug, Default, Clone, Eq, PartialEq)] @@ -39,6 +131,7 @@ impl FeeStructure { sol_per_write_lock: f64, compute_fee_bins: Vec<(u64, f64)>, ) -> Self { + trace!("Creating FeeStructure with sol_per_signature: {}", sol_per_signature); let compute_fee_bins = compute_fee_bins .iter() .map(|(limit, sol)| FeeBin { @@ -54,7 +147,7 @@ impl FeeStructure { } pub fn get_max_fee(&self, num_signatures: u64, num_write_locks: u64) -> u64 { - num_signatures + let max_fee = num_signatures .saturating_mul(self.lamports_per_signature) .saturating_add(num_write_locks.saturating_mul(self.lamports_per_write_lock)) .saturating_add( @@ -62,17 +155,21 @@ impl FeeStructure { .last() .map(|bin| bin.fee) .unwrap_or_default(), - ) + ); + trace!("Calculated max_fee: {}", max_fee); + max_fee } pub fn calculate_memory_usage_cost( loaded_accounts_data_size_limit: usize, heap_cost: u64, ) -> u64 { - (loaded_accounts_data_size_limit as u64) + let memory_usage_cost = (loaded_accounts_data_size_limit as u64) .saturating_add(ACCOUNT_DATA_COST_PAGE_SIZE.saturating_sub(1)) .saturating_div(ACCOUNT_DATA_COST_PAGE_SIZE) - .saturating_mul(heap_cost) + .saturating_mul(heap_cost); + trace!("Calculated memory_usage_cost: {}", memory_usage_cost); + memory_usage_cost } /// Calculate fee for `SanitizedMessage` @@ -80,61 +177,37 @@ impl FeeStructure { pub fn calculate_fee( &self, message: &SanitizedMessage, - lamports_per_signature: u64, + _lamports_per_signature: u64, budget_limits: &FeeBudgetLimits, - include_loaded_account_data_size_in_fee: bool, + _include_loaded_account_data_size_in_fee: bool, ) -> u64 { - // Fee based on compute units and signatures - let congestion_multiplier = if lamports_per_signature == 0 { - 0.0 // test only - } else { - 1.0 // multiplier that has no effect - }; + // If the message contains the vote program, set the total fee to 0. + if message.account_keys().iter() + .any(|key| key == &solana_sdk::vote::program::id()) { + return 0 + } - let signature_fee = message - .num_signatures() - .saturating_mul(self.lamports_per_signature); - let write_lock_fee = message - .num_write_locks() - .saturating_mul(self.lamports_per_write_lock); - - // `compute_fee` covers costs for both requested_compute_units and - // requested_loaded_account_data_size - let loaded_accounts_data_size_cost = if include_loaded_account_data_size_in_fee { - FeeStructure::calculate_memory_usage_cost( - budget_limits.loaded_accounts_data_size_limit, - budget_limits.heap_cost, - ) + let derived_cu = get_transaction_cost(&message, budget_limits); + let compute_unit_price = get_compute_unit_price_from_message(&message); + let adjusted_compute_unit_price = if derived_cu < 1000 && compute_unit_price < 1_000_000 { + 1_000_000 } else { - 0_u64 + compute_unit_price }; - let total_compute_units = - loaded_accounts_data_size_cost.saturating_add(budget_limits.compute_unit_limit); - let compute_fee = self - .compute_fee_bins - .iter() - .find(|bin| total_compute_units <= bin.limit) - .map(|bin| bin.fee) - .unwrap_or_else(|| { - self.compute_fee_bins - .last() - .map(|bin| bin.fee) - .unwrap_or_default() - }); - - ((budget_limits - .prioritization_fee - .saturating_add(signature_fee) - .saturating_add(write_lock_fee) - .saturating_add(compute_fee) as f64) - * congestion_multiplier) - .round() as u64 + + let total_fee = derived_cu + .saturating_mul(10) // ensures multiplication doesn't overflow + .saturating_add(derived_cu.saturating_mul(adjusted_compute_unit_price) + .saturating_div(1_000_000)); // change to 1_000_000 to convert to micro lamports + + trace!("total_fee: {}", total_fee); + total_fee } } impl Default for FeeStructure { fn default() -> Self { - Self::new(0.000005, 0.0, vec![(1_400_000, 0.0)]) + Self::new(0.000000005, 0.0, vec![(1_400_000, 0.0)]) } } @@ -181,3 +254,4 @@ mod tests { ); } } +