From 4e366ac137d48881b5612a707b902a755bbd4d65 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Mon, 31 Aug 2020 21:51:52 -0700 Subject: [PATCH] Ensure that the spl-token 2 native mint account is owned by the spl-token 2 program. Workaround for https://github.com/solana-labs/solana-program-library/issues/374 until spl-token 3 is shipped (cherry picked from commit 7341e600434df5465268dae40b4635cc09220328) # Conflicts: # core/src/rpc_service.rs # runtime/src/bank.rs --- core/src/non_circulating_supply.rs | 5 +- core/src/rpc.rs | 6 +- core/src/rpc_service.rs | 12 +- runtime/src/bank.rs | 362 ++++++++++++++++++++++++++++- 4 files changed, 378 insertions(+), 7 deletions(-) diff --git a/core/src/non_circulating_supply.rs b/core/src/non_circulating_supply.rs index cbc854f2719ff9..bb17f98430f80f 100644 --- a/core/src/non_circulating_supply.rs +++ b/core/src/non_circulating_supply.rs @@ -96,7 +96,9 @@ solana_sdk::pubkeys!( mod tests { use super::*; use solana_sdk::{ - account::Account, epoch_schedule::EpochSchedule, genesis_config::GenesisConfig, + account::Account, + epoch_schedule::EpochSchedule, + genesis_config::{GenesisConfig, OperatingMode}, }; use solana_stake_program::stake_state::{Authorized, Lockup, Meta, StakeState}; use std::{collections::BTreeMap, sync::Arc}; @@ -147,6 +149,7 @@ mod tests { let genesis_config = GenesisConfig { accounts, epoch_schedule: EpochSchedule::new(slots_per_epoch), + operating_mode: OperatingMode::Stable, ..GenesisConfig::default() }; let mut bank = Arc::new(Bank::new(&genesis_config)); diff --git a/core/src/rpc.rs b/core/src/rpc.rs index 51c0d15f39777f..783366a657fefd 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -2855,7 +2855,7 @@ pub mod tests { let largest_accounts: Vec = serde_json::from_value(json["result"]["value"].clone()) .expect("actual response deserialization"); - assert_eq!(largest_accounts.len(), 19); + assert_eq!(largest_accounts.len(), 20); // Get Alice balance let req = format!( @@ -2892,7 +2892,7 @@ pub mod tests { let largest_accounts: Vec = serde_json::from_value(json["result"]["value"].clone()) .expect("actual response deserialization"); - assert_eq!(largest_accounts.len(), 18); + assert_eq!(largest_accounts.len(), 19); let req = r#"{"jsonrpc":"2.0","id":1,"method":"getLargestAccounts","params":[{"filter":"nonCirculating"}]}"#; let res = io.handle_request_sync(&req, meta); let json: Value = serde_json::from_str(&res.unwrap()).unwrap(); @@ -4853,7 +4853,7 @@ pub mod tests { .expect("actual response deserialization"); let accounts: Vec = serde_json::from_value(result["result"].clone()).unwrap(); - assert_eq!(accounts.len(), 3); + assert_eq!(accounts.len(), 4); // Test returns only mint accounts let req = format!( diff --git a/core/src/rpc_service.rs b/core/src/rpc_service.rs index cf3ffdb5540535..7e22cf34c35029 100644 --- a/core/src/rpc_service.rs +++ b/core/src/rpc_service.rs @@ -384,8 +384,15 @@ mod tests { get_tmp_ledger_path, snapshot_utils::SnapshotVersion, }; +<<<<<<< HEAD use solana_runtime::bank::Bank; use solana_sdk::signature::Signer; +======= + use solana_runtime::{ + bank::Bank, bank_forks::CompressionType, snapshot_utils::SnapshotVersion, + }; + use solana_sdk::{genesis_config::OperatingMode, signature::Signer}; +>>>>>>> 7341e6004... Ensure that the spl-token 2 native mint account is owned by the spl-token 2 program. use std::net::{IpAddr, Ipv4Addr}; #[test] @@ -440,7 +447,10 @@ mod tests { } fn create_bank_forks() -> Arc> { - let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000); + let GenesisConfigInfo { + mut genesis_config, .. + } = create_genesis_config(10_000); + genesis_config.operating_mode = OperatingMode::Stable; let bank = Bank::new(&genesis_config); Arc::new(RwLock::new(BankForks::new(bank))) } diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 29d575aeeda42b..ebea2384fbb2e7 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -45,7 +45,9 @@ use solana_sdk::{ hash::{extend_and_hash, hashv, Hash}, incinerator, inflation::Inflation, - native_loader, nonce, + native_loader, + native_token::sol_to_lamports, + nonce, program_utils::limited_deserialize, pubkey::Pubkey, sanitize::Sanitize, @@ -70,6 +72,29 @@ use std::{ sync::{Arc, RwLock, RwLockReadGuard}, }; +// Partial SPL Token v2.0.x declarations inlined to avoid an external dependency on the spl-token crate +pub mod inline_spl_token_v2_0 { + solana_sdk::declare_id!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); + pub mod native_mint { + solana_sdk::declare_id!("So11111111111111111111111111111111111111112"); + + /* + Mint { + mint_authority: COption::None, + supply: 0, + decimals: 9, + is_initialized: true, + freeze_authority: COption::None, + } + */ + pub const ACCOUNT_DATA: [u8; 82] = [ + 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 1, 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, 0, 0, 0, 0, + ]; + } +} + pub const SECONDS_PER_YEAR: f64 = 365.25 * 24.0 * 60.0 * 60.0; pub const MAX_LEADER_SCHEDULE_STAKES: Epoch = 5; @@ -2877,6 +2902,109 @@ impl Bank { consumed_budget.saturating_sub(budget_recovery_delta) } +<<<<<<< HEAD +======= + // This is called from snapshot restore AND for each epoch boundary + // The entire code path herein must be idempotent + fn apply_feature_activations(&mut self, init_finish_or_warp: bool, initiate_callback: bool) { + self.ensure_builtins(init_finish_or_warp); + self.reinvoke_entered_epoch_callback(initiate_callback); + self.recheck_cross_program_support(); + self.recheck_compute_budget(); + self.reconfigure_token2_native_mint(); + } + + fn ensure_builtins(&mut self, init_or_warp: bool) { + for (program, start_epoch) in get_builtins(self.operating_mode()) { + let should_populate = init_or_warp && self.epoch() >= start_epoch + || !init_or_warp && self.epoch() == start_epoch; + if should_populate { + self.add_builtin(&program.name, program.id, program.entrypoint); + } + } + } + + fn reinvoke_entered_epoch_callback(&mut self, initiate: bool) { + if let Some(entered_epoch_callback) = + self.entered_epoch_callback.clone().read().unwrap().as_ref() + { + entered_epoch_callback(self, initiate) + } + } + + fn recheck_cross_program_support(self: &mut Bank) { + if OperatingMode::Stable == self.operating_mode() { + self.set_cross_program_support(self.epoch() >= 63); + } else { + self.set_cross_program_support(true); + } + } + + fn recheck_compute_budget(self: &mut Bank) { + let compute_budget = if OperatingMode::Stable == self.operating_mode() { + if self.epoch() >= u64::MAX - 1 { + ComputeBudget::default() + } else { + // Original + ComputeBudget { + max_units: 100_000, + log_units: 0, + log_64_units: 0, + create_program_address_units: 0, + invoke_units: 0, + max_invoke_depth: 2, + } + } + } else { + ComputeBudget::default() + }; + self.set_compute_budget(compute_budget); + } + + fn reconfigure_token2_native_mint(self: &mut Bank) { + let reconfigure_token2_native_mint = match self.operating_mode() { + OperatingMode::Development => true, + OperatingMode::Preview => self.epoch() == 95, + OperatingMode::Stable => self.epoch() == 75, + }; + + if reconfigure_token2_native_mint { + let mut native_mint_account = solana_sdk::account::Account { + owner: inline_spl_token_v2_0::id(), + data: inline_spl_token_v2_0::native_mint::ACCOUNT_DATA.to_vec(), + lamports: sol_to_lamports(1.), + executable: false, + rent_epoch: self.epoch() + 1, + }; + + // As a workaround for + // https://github.com/solana-labs/solana-program-library/issues/374, ensure that the + // spl-token 2 native mint account is owned by the spl-token 2 program. + let store = if let Some(existing_native_mint_account) = + self.get_account(&inline_spl_token_v2_0::native_mint::id()) + { + if existing_native_mint_account.owner == solana_sdk::system_program::id() { + native_mint_account.lamports = existing_native_mint_account.lamports; + true + } else { + false + } + } else { + self.capitalization + .fetch_add(native_mint_account.lamports, Ordering::Relaxed); + true + }; + + if store { + self.store_account( + &inline_spl_token_v2_0::native_mint::id(), + &native_mint_account, + ); + } + } + } + +>>>>>>> 7341e6004... Ensure that the spl-token 2 native mint account is owned by the spl-token 2 program. fn fix_recent_blockhashes_sysvar_delay(&self) -> bool { let activation_slot = match self.operating_mode() { OperatingMode::Development => 0, @@ -3087,6 +3215,7 @@ mod tests { accounts: (0..42) .map(|_| (Pubkey::new_rand(), Account::new(42, 0, &Pubkey::default()))) .collect(), + operating_mode: OperatingMode::Stable, ..GenesisConfig::default() })); assert_eq!(bank.capitalization(), 42 * 42); @@ -4552,6 +4681,7 @@ mod tests { hashes_per_tick: None, target_tick_count: None, }, + operating_mode: OperatingMode::Stable, ..GenesisConfig::default() })); @@ -4663,6 +4793,7 @@ mod tests { hashes_per_tick: None, target_tick_count: None, }, + operating_mode: OperatingMode::Stable, ..GenesisConfig::default() })); @@ -7653,6 +7784,7 @@ mod tests { &[], ); genesis_config.creation_time = 0; + genesis_config.operating_mode = OperatingMode::Stable; let mut bank = Arc::new(Bank::new(&genesis_config)); // Check a few slots, cross an epoch boundary assert_eq!(bank.get_slots_in_epoch(0), 32); @@ -7661,25 +7793,41 @@ mod tests { if bank.slot == 0 { assert_eq!( bank.hash().to_string(), +<<<<<<< HEAD "HsNNxYe9FjPZtW8zNwSrLsB6d8YXoWqhpegE328C5Wvy", +======= + "HrxvKJxPhzgXgoJ2Rg91acrXWki6SErJk1sfxfTDWsZt" +>>>>>>> 7341e6004... Ensure that the spl-token 2 native mint account is owned by the spl-token 2 program. ); } if bank.slot == 32 { assert_eq!( bank.hash().to_string(), +<<<<<<< HEAD "9rsnmUCTg2wFnKVnaUSEQcSicvPUyXPTmbsakB2o3eTu" +======= + "AemCVcpsULuA76fpDYzs74futtMQtNNiF1gz3VSVP4AS" +>>>>>>> 7341e6004... Ensure that the spl-token 2 native mint account is owned by the spl-token 2 program. ); } if bank.slot == 64 { assert_eq!( bank.hash().to_string(), +<<<<<<< HEAD "B1JNAeiR89kcqwqeSSQXFN3spcYW2qVJwuu9hQ9dxn1V" +======= + "4ECxp4u4SRwn2w7YUj28nnADMx7kQ2gYRB7PA617eWu6" +>>>>>>> 7341e6004... Ensure that the spl-token 2 native mint account is owned by the spl-token 2 program. ); } if bank.slot == 128 { assert_eq!( bank.hash().to_string(), +<<<<<<< HEAD "9iUEmDnWQmjtkoMuR3hJAwQMpCGwHaJAEsfK131rww8y" +======= + "AeHGi2VWQ7gWh6KkMQjrhY1ZrLUedgMxfvPRR82dyg6L" +>>>>>>> 7341e6004... Ensure that the spl-token 2 native mint account is owned by the spl-token 2 program. ); break; } @@ -7770,7 +7918,7 @@ mod tests { .map(|_| bank.process_stale_slot_with_budget(0, force_to_return_alive_account)) .collect::>(); consumed_budgets.sort(); - assert_eq!(consumed_budgets, vec![0, 1, 8]); + assert_eq!(consumed_budgets, vec![0, 1, 9]); } #[test] @@ -7864,4 +8012,214 @@ mod tests { ); assert_eq!(bank.get_balance(&mint_keypair.pubkey()), 496); // no transaction fee charged } +<<<<<<< HEAD +======= + + #[test] + fn test_finish_init() { + let (genesis_config, _mint_keypair) = create_genesis_config(100_000); + let mut bank = Bank::new(&genesis_config); + bank.message_processor = MessageProcessor::default(); + bank.message_processor.set_cross_program_support(false); + + // simulate bank is just after deserialized from snapshot + bank.finish_init(); + + assert_eq!(bank.message_processor.get_cross_program_support(), true); + } + + #[test] + fn test_add_builtin_program_no_overwrite() { + let (genesis_config, _mint_keypair) = create_genesis_config(100_000); + + fn mock_ix_processor( + _pubkey: &Pubkey, + _ka: &[KeyedAccount], + _data: &[u8], + ) -> std::result::Result<(), InstructionError> { + Ok(()) + } + + let slot = 123; + let program_id = Pubkey::new_rand(); + + let mut bank = Arc::new(Bank::new_from_parent( + &Arc::new(Bank::new(&genesis_config)), + &Pubkey::default(), + slot, + )); + assert_eq!(bank.get_account_modified_slot(&program_id), None); + + Arc::get_mut(&mut bank).unwrap().add_builtin_program( + "mock_program", + program_id, + mock_ix_processor, + ); + assert_eq!(bank.get_account_modified_slot(&program_id).unwrap().1, slot); + + let mut bank = Arc::new(new_from_parent(&bank)); + Arc::get_mut(&mut bank).unwrap().add_builtin_program( + "mock_program", + program_id, + mock_ix_processor, + ); + assert_eq!(bank.get_account_modified_slot(&program_id).unwrap().1, slot); + } + + #[test] + fn test_add_builtin_loader_no_overwrite() { + let (genesis_config, _mint_keypair) = create_genesis_config(100_000); + + fn mock_ix_processor( + _pubkey: &Pubkey, + _ka: &[KeyedAccount], + _data: &[u8], + _context: &mut dyn solana_sdk::entrypoint_native::InvokeContext, + ) -> std::result::Result<(), InstructionError> { + Ok(()) + } + + let slot = 123; + let loader_id = Pubkey::new_rand(); + + let mut bank = Arc::new(Bank::new_from_parent( + &Arc::new(Bank::new(&genesis_config)), + &Pubkey::default(), + slot, + )); + assert_eq!(bank.get_account_modified_slot(&loader_id), None); + + Arc::get_mut(&mut bank).unwrap().add_builtin_loader( + "mock_program", + loader_id, + mock_ix_processor, + ); + assert_eq!(bank.get_account_modified_slot(&loader_id).unwrap().1, slot); + + let mut bank = Arc::new(new_from_parent(&bank)); + Arc::get_mut(&mut bank).unwrap().add_builtin_loader( + "mock_program", + loader_id, + mock_ix_processor, + ); + assert_eq!(bank.get_account_modified_slot(&loader_id).unwrap().1, slot); + } + + #[test] + fn test_add_native_program_no_overwrite() { + let (genesis_config, _mint_keypair) = create_genesis_config(100_000); + + let slot = 123; + let program_id = Pubkey::new_rand(); + + let mut bank = Arc::new(Bank::new_from_parent( + &Arc::new(Bank::new(&genesis_config)), + &Pubkey::default(), + slot, + )); + assert_eq!(bank.get_account_modified_slot(&program_id), None); + + Arc::get_mut(&mut bank) + .unwrap() + .add_native_program("mock_program", &program_id); + assert_eq!(bank.get_account_modified_slot(&program_id).unwrap().1, slot); + + let mut bank = Arc::new(new_from_parent(&bank)); + Arc::get_mut(&mut bank) + .unwrap() + .add_native_program("mock_program", &program_id); + assert_eq!(bank.get_account_modified_slot(&program_id).unwrap().1, slot); + } + + #[test] + #[should_panic( + expected = "Can't change frozen bank by adding not-existing new native \ + program (mock_program, CiXgo2KHKSDmDnV1F6B69eWFgNAPiSBjjYvfB4cvRNre). \ + Maybe, inconsistent program activation is detected on snapshot restore?" + )] + fn test_add_native_program_after_frozen() { + use std::str::FromStr; + let (genesis_config, _mint_keypair) = create_genesis_config(100_000); + + let slot = 123; + let program_id = Pubkey::from_str("CiXgo2KHKSDmDnV1F6B69eWFgNAPiSBjjYvfB4cvRNre").unwrap(); + + let mut bank = Arc::new(Bank::new_from_parent( + &Arc::new(Bank::new(&genesis_config)), + &Pubkey::default(), + slot, + )); + bank.freeze(); + + Arc::get_mut(&mut bank) + .unwrap() + .add_native_program("mock_program", &program_id); + } + + #[test] + fn test_reconfigure_token2_native_mint() { + solana_logger::setup(); + + let mut genesis_config = + create_genesis_config_with_leader(5, &Pubkey::new_rand(), 0).genesis_config; + + // OperatingMode::Development - Native mint exists immediately + assert_eq!(genesis_config.operating_mode, OperatingMode::Development); + let bank = Arc::new(Bank::new(&genesis_config)); + assert_eq!( + bank.get_balance(&inline_spl_token_v2_0::native_mint::id()), + 1000000000 + ); + + // OperatingMode::Preview - Native mint blinks into existence at epoch 95 + genesis_config.operating_mode = OperatingMode::Preview; + let bank = Arc::new(Bank::new(&genesis_config)); + assert_eq!( + bank.get_balance(&inline_spl_token_v2_0::native_mint::id()), + 0 + ); + bank.deposit(&inline_spl_token_v2_0::native_mint::id(), 4200000000); + + let bank = Bank::new_from_parent( + &bank, + &Pubkey::default(), + genesis_config.epoch_schedule.get_first_slot_in_epoch(95), + ); + + let native_mint_account = bank + .get_account(&inline_spl_token_v2_0::native_mint::id()) + .unwrap(); + assert_eq!(native_mint_account.data.len(), 82); + assert_eq!( + bank.get_balance(&inline_spl_token_v2_0::native_mint::id()), + 4200000000 + ); + assert_eq!(native_mint_account.owner, inline_spl_token_v2_0::id()); + + // OperatingMode::Stable - Native mint blinks into existence at epoch 75 + genesis_config.operating_mode = OperatingMode::Stable; + let bank = Arc::new(Bank::new(&genesis_config)); + assert_eq!( + bank.get_balance(&inline_spl_token_v2_0::native_mint::id()), + 0 + ); + bank.deposit(&inline_spl_token_v2_0::native_mint::id(), 4200000000); + + let bank = Bank::new_from_parent( + &bank, + &Pubkey::default(), + genesis_config.epoch_schedule.get_first_slot_in_epoch(75), + ); + + let native_mint_account = bank + .get_account(&inline_spl_token_v2_0::native_mint::id()) + .unwrap(); + assert_eq!(native_mint_account.data.len(), 82); + assert_eq!( + bank.get_balance(&inline_spl_token_v2_0::native_mint::id()), + 4200000000 + ); + assert_eq!(native_mint_account.owner, inline_spl_token_v2_0::id()); + } +>>>>>>> 7341e6004... Ensure that the spl-token 2 native mint account is owned by the spl-token 2 program. }