diff --git a/src/accounts_database/accounts_db.rs b/src/accounts_database/accounts_db.rs index 714af62..99599a0 100644 --- a/src/accounts_database/accounts_db.rs +++ b/src/accounts_database/accounts_db.rs @@ -14,7 +14,6 @@ use super::sysvar_tracker::SysvarTracker; pub struct AccountsDB { pub(crate) accounts: HashMap, pub(crate) permanent_accounts: HashMap, - pub(crate) programs: HashMap, pub(crate) sysvars: HashMap, pub(crate) sysvar_tracker: SysvarTracker, } @@ -28,9 +27,6 @@ impl AccountsDB { .get(pubkey) .map(|acc| acc.to_owned()) } - pub(crate) fn get_program(&self, pubkey: &Pubkey) -> Option { - self.programs.get(pubkey).map(|acc| acc.to_owned()) - } pub(crate) fn get_sysvar_account(&self, pubkey: &Pubkey) -> Option { self.sysvars.get(pubkey).map(|acc| acc.to_owned()) } @@ -57,9 +53,6 @@ impl AccountsDB { .permanent_accounts .insert(pubkey.to_owned(), account.to_owned()); } - pub(crate) fn set_program(&mut self, pubkey: &Pubkey, account: &AccountSharedData) { - let _ = self.programs.insert(pubkey.to_owned(), account.to_owned()); - } pub(crate) fn set_sysvar(&mut self, sysvar: &T) { let account = AccountSharedData::new_data(1, &sysvar, &solana_sdk_ids::sysvar::id()).unwrap(); diff --git a/src/accounts_database/methods.rs b/src/accounts_database/methods.rs index c830b28..bbd244c 100644 --- a/src/accounts_database/methods.rs +++ b/src/accounts_database/methods.rs @@ -16,8 +16,6 @@ impl AccountsDB { Some(account.to_owned()) } else if let Some(permanent_account) = self.get_permanent_account(pubkey) { Some(permanent_account.to_owned()) - } else if let Some(program) = self.get_program(pubkey) { - Some(program) } else { if pubkey.eq(&Clock::id()) && update_clock { self.update_clock(); diff --git a/src/builder.rs b/src/builder.rs index 14489f4..7e27f87 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -5,7 +5,6 @@ use crate::trident_svm_log::turn_off_solana_logging; use crate::types::trident_account::TridentAccountSharedData; #[cfg(feature = "syscall-v2")] use crate::types::trident_entrypoint::TridentEntrypoint; -use crate::types::trident_program::TridentProgram; #[derive(Default)] pub struct TridentSVMConfig { @@ -15,7 +14,6 @@ pub struct TridentSVMConfig { debug_file_logs: Option, #[cfg(feature = "syscall-v2")] program_entrypoints: Vec, - program_binaries: Vec, permanent_accounts: Vec, } @@ -47,11 +45,6 @@ impl TridentSVMBuilder { self } - pub fn with_sbf_programs(&mut self, programs: Vec) -> &Self { - self.config.program_binaries = programs; - self - } - pub fn with_permanent_accounts(&mut self, accounts: Vec) -> &Self { self.config.permanent_accounts = accounts; self @@ -88,10 +81,6 @@ impl TridentSVMBuilder { svm.deploy_entrypoint_program(entry); } - for program in &self.config.program_binaries { - svm.deploy_binary_program(program); - } - for account in &self.config.permanent_accounts { svm.accounts .set_permanent_account(&account.address, &account.account); diff --git a/src/methods/trident_svm_programs.rs b/src/methods/trident_svm_programs.rs index c987da2..2e89ffa 100644 --- a/src/methods/trident_svm_programs.rs +++ b/src/methods/trident_svm_programs.rs @@ -1,72 +1,17 @@ -use solana_account::AccountSharedData; -use solana_account::WritableAccount; -use solana_loader_v3_interface::state::UpgradeableLoaderState; -use solana_sysvar::rent::Rent; - #[cfg(feature = "syscall-v2")] use crate::types::trident_entrypoint::TridentEntrypoint; #[cfg(feature = "syscall-v2")] use solana_program_runtime::loaded_programs::ProgramCacheEntry; use crate::trident_svm::TridentSVM; -use crate::types::trident_program::TridentProgram; impl TridentSVM { - pub fn deploy_binary_program(&mut self, program: &TridentProgram) { - let rent = Rent::default(); - - let program_account = &program.program_id; - - let program_data_account = - solana_loader_v3_interface::get_program_data_address(program_account); - - let state = UpgradeableLoaderState::Program { - programdata_address: program_data_account, - }; - - let buffer = bincode::serialize(&state).unwrap(); - let account_data = AccountSharedData::create( - rent.minimum_balance(buffer.len()), - buffer, - solana_sdk_ids::bpf_loader_upgradeable::id(), - true, - Default::default(), - ); - - self.accounts.set_program(program_account, &account_data); - - let state = UpgradeableLoaderState::ProgramData { - slot: 0, - upgrade_authority_address: program.authority, - }; - let mut header = bincode::serialize(&state).unwrap(); - - let mut complement = vec![ - 0; - std::cmp::max( - 0, - UpgradeableLoaderState::size_of_programdata_metadata().saturating_sub(header.len()) - ) - ]; - - let mut buffer: Vec = program.data.to_vec(); - header.append(&mut complement); - header.append(&mut buffer); - - let account_data = AccountSharedData::create( - rent.minimum_balance(header.len()), - header, - solana_sdk_ids::bpf_loader_upgradeable::id(), - true, - Default::default(), - ); - - self.accounts - .set_program(&program_data_account, &account_data); - } - #[cfg(feature = "syscall-v2")] pub fn deploy_entrypoint_program(&mut self, program: &TridentEntrypoint) { + use solana_account::{AccountSharedData, WritableAccount}; + use solana_loader_v3_interface::state::UpgradeableLoaderState; + use solana_rent::Rent; + use crate::utils::create_loadable_account_for_test; let entry = match program.entry { @@ -74,7 +19,7 @@ impl TridentSVM { None => panic!("Native programs have to have entry specified"), }; - self.accounts.set_program( + self.accounts.set_permanent_account( &program.program_id, &create_loadable_account_for_test("program-name"), ); @@ -111,7 +56,7 @@ impl TridentSVM { ); self.accounts - .set_program(&program_data_account, &account_data); + .set_permanent_account(&program_data_account, &account_data); self.processor.add_builtin( self, diff --git a/src/trident_svm.rs b/src/trident_svm.rs index f67db42..2858321 100644 --- a/src/trident_svm.rs +++ b/src/trident_svm.rs @@ -41,12 +41,12 @@ use crate::accounts_database::accounts_db::AccountsDB; use crate::builder::TridentSVMBuilder; use crate::trident_fork_graphs::TridentForkGraph; +use crate::types::trident_account::TridentAccountSharedData; use crate::utils; use solana_builtins::BUILTINS; use solana_program_runtime::execution_budget::SVMTransactionExecutionBudget; -use crate::types::trident_program::TridentProgram; use crate::utils::get_current_timestamp; pub struct TridentSVM { @@ -181,7 +181,7 @@ impl TridentSVM { } fn with_builtins(mut self) -> Self { BUILTINS.iter().for_each(|builtint| { - self.accounts.set_program( + self.accounts.set_permanent_account( &builtint.program_id, &utils::create_loadable_account_for_test(builtint.name), ); @@ -197,65 +197,84 @@ impl TridentSVM { self } fn with_solana_program_library(mut self) -> Self { - let spl_token = TridentProgram::new( + // SPL Token is deployed with Loader v2 + let spl_token = TridentAccountSharedData::loader_v2_program( pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), - None, - include_bytes!("solana-program-library/spl-token-mainnet.so").to_vec(), + include_bytes!("solana-program-library/spl-token-mainnet.so"), ); - self.deploy_binary_program(&spl_token); + self.accounts + .set_permanent_account(&spl_token.address, &spl_token.account); - // SPL Token 2022 added for new Token 2022 Trident features - let spl_token_2022 = TridentProgram::new( + // SPL Token 2022 is deployed with Loader v3 + let spl_token_2022 = TridentAccountSharedData::loader_v3_program( pubkey!("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"), + include_bytes!("solana-program-library/spl-2022-token-mainnet.so"), None, - include_bytes!("solana-program-library/spl-2022-token-mainnet.so").to_vec(), ); - self.deploy_binary_program(&spl_token_2022); + for account in spl_token_2022 { + self.accounts + .set_permanent_account(&account.address, &account.account); + } - let associated_token_program = TridentProgram::new( + // Associated Token Program is deployed with Loader v2 + let associated_token_program = TridentAccountSharedData::loader_v2_program( pubkey!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"), - None, - include_bytes!("solana-program-library/associated-token-program-mainnet.so").to_vec(), + include_bytes!("solana-program-library/associated-token-program-mainnet.so"), ); - self.deploy_binary_program(&associated_token_program); + self.accounts.set_permanent_account( + &associated_token_program.address, + &associated_token_program.account, + ); - let metaplex_token_metadata = TridentProgram::new( + // Metaplex Token Metadata is deployed with Loader v3 + let metaplex_token_metadata = TridentAccountSharedData::loader_v3_program( pubkey!("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"), + include_bytes!("solana-program-library/metaplex-token-metadata.so"), None, - include_bytes!("solana-program-library/metaplex-token-metadata.so").to_vec(), ); - self.deploy_binary_program(&metaplex_token_metadata); + for account in metaplex_token_metadata { + self.accounts + .set_permanent_account(&account.address, &account.account); + } - // Interesting to have an Oracle program for testing programs with Price feed manipulation - // Another good program would be Pyth Oracle, which is good for cross-chain price feeds - let chainlink_oracle = TridentProgram::new( + // Chainlink Oracle is deployed with Loader v3 + let chainlink_oracle = TridentAccountSharedData::loader_v3_program( pubkey!("HEvSKofvBgfaexv23kMabbYqxasxU3mQ4ibBMEmJWHny"), + include_bytes!("solana-program-library/chainlink-oracle.so"), None, - include_bytes!("solana-program-library/chainlink-oracle.so").to_vec(), ); - self.deploy_binary_program(&chainlink_oracle); + for account in chainlink_oracle { + self.accounts + .set_permanent_account(&account.address, &account.account); + } - // Native Stake Pool (SPL Stake Pool): - // This program is used for managing stake pools, which can be useful for testing programs that interact with staking - let spl_stake_pool = TridentProgram::new( + // SPL Stake Pool is deployed with Loader v3 + let spl_stake_pool = TridentAccountSharedData::loader_v3_program( pubkey!("SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy"), + include_bytes!("solana-program-library/spl-stake-pool.so"), None, - include_bytes!("solana-program-library/spl-stake-pool.so").to_vec(), ); - self.deploy_binary_program(&spl_stake_pool); - // Could be interesting to have candy machine for testing programs that interact with it Minting NFTs - let metaplex_candy_machine_v3 = TridentProgram::new( + for account in spl_stake_pool { + self.accounts + .set_permanent_account(&account.address, &account.account); + } + + // Metaplex Candy Machine V3 is deployed with Loader v3 + let metaplex_candy_machine_v3 = TridentAccountSharedData::loader_v3_program( pubkey!("CndyV3LdqHUfDLmE5naZjVN8rBZz4tqhdefbAnjHG3JR"), + include_bytes!("solana-program-library/metaplex-candy-machine-v3.so"), None, - include_bytes!("solana-program-library/metaplex-candy-machine-v3.so").to_vec(), ); - self.deploy_binary_program(&metaplex_candy_machine_v3); + for account in metaplex_candy_machine_v3 { + self.accounts + .set_permanent_account(&account.address, &account.account); + } self } diff --git a/src/types/mod.rs b/src/types/mod.rs index 43c595f..df78057 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -2,4 +2,3 @@ pub mod transaction_result; pub mod trident_account; #[cfg(feature = "syscall-v2")] pub mod trident_entrypoint; -pub mod trident_program; diff --git a/src/types/trident_account.rs b/src/types/trident_account.rs index 41151ad..5c5ec5a 100644 --- a/src/types/trident_account.rs +++ b/src/types/trident_account.rs @@ -1,6 +1,10 @@ use solana_account::AccountSharedData; use solana_pubkey::Pubkey; +use solana_account::WritableAccount; +use solana_loader_v3_interface::state::UpgradeableLoaderState; +use solana_sysvar::rent::Rent; + pub struct TridentAccountSharedData { pub address: Pubkey, pub account: AccountSharedData, @@ -9,4 +13,84 @@ impl TridentAccountSharedData { pub fn new(address: Pubkey, account: AccountSharedData) -> TridentAccountSharedData { Self { address, account } } + + pub fn loader_v2_program(address: Pubkey, data: &[u8]) -> TridentAccountSharedData { + let rent = Rent::default(); + + let account_data = AccountSharedData::create( + rent.minimum_balance(data.len()), + data.to_vec(), + solana_sdk_ids::bpf_loader::id(), + true, + Default::default(), + ); + + TridentAccountSharedData::new(address, account_data) + } + + pub fn loader_v3_program( + address: Pubkey, + data: &[u8], + authority: Option, + ) -> Vec { + let mut accounts = Vec::new(); + + let rent = Rent::default(); + + let program_account = &address; + + let program_data_account = + solana_loader_v3_interface::get_program_data_address(program_account); + + let state = UpgradeableLoaderState::Program { + programdata_address: program_data_account, + }; + + let buffer = bincode::serialize(&state).unwrap(); + let account_data = AccountSharedData::create( + rent.minimum_balance(buffer.len()), + buffer, + solana_sdk_ids::bpf_loader_upgradeable::id(), + true, + Default::default(), + ); + + accounts.push(TridentAccountSharedData::new( + *program_account, + account_data, + )); + + let state = UpgradeableLoaderState::ProgramData { + slot: 0, + upgrade_authority_address: authority, + }; + let mut header = bincode::serialize(&state).unwrap(); + + let mut complement = vec![ + 0; + std::cmp::max( + 0, + UpgradeableLoaderState::size_of_programdata_metadata().saturating_sub(header.len()) + ) + ]; + + let mut buffer: Vec = data.to_vec(); + header.append(&mut complement); + header.append(&mut buffer); + + let account_data = AccountSharedData::create( + rent.minimum_balance(header.len()), + header, + solana_sdk_ids::bpf_loader_upgradeable::id(), + true, + Default::default(), + ); + + accounts.push(TridentAccountSharedData::new( + program_data_account, + account_data, + )); + + accounts + } } diff --git a/src/types/trident_program.rs b/src/types/trident_program.rs deleted file mode 100644 index d5083ad..0000000 --- a/src/types/trident_program.rs +++ /dev/null @@ -1,16 +0,0 @@ -use solana_pubkey::Pubkey; - -pub struct TridentProgram { - pub(crate) program_id: Pubkey, - pub(crate) authority: Option, - pub(crate) data: Vec, -} -impl TridentProgram { - pub fn new(program_id: Pubkey, authority: Option, data: Vec) -> Self { - Self { - program_id, - authority, - data, - } - } -}