trident_svm/
trident_svm.rs

1use std::sync::Arc;
2use std::sync::RwLock;
3
4use solana_bpf_loader_program::syscalls::create_program_runtime_environment_v1;
5use solana_bpf_loader_program::syscalls::create_program_runtime_environment_v2;
6
7use solana_program_runtime::loaded_programs::ProgramCacheEntry;
8
9use solana_account::AccountSharedData;
10use solana_account::ReadableAccount;
11use solana_clock::Clock;
12use solana_epoch_rewards::EpochRewards;
13use solana_epoch_schedule::EpochSchedule;
14use solana_hash::Hash;
15use solana_keypair::Keypair;
16use solana_pubkey::pubkey;
17use solana_pubkey::Pubkey;
18use solana_rent::Rent;
19use solana_signer::Signer;
20use solana_slot_hashes::SlotHashes;
21use solana_svm_feature_set::SVMFeatureSet;
22#[allow(deprecated)]
23use solana_sysvar::fees::Fees;
24#[allow(deprecated)]
25use solana_sysvar::recent_blockhashes::IterItem;
26#[allow(deprecated)]
27use solana_sysvar::recent_blockhashes::RecentBlockhashes;
28
29use solana_slot_history::SlotHistory;
30
31use solana_stake_interface::stake_history::StakeHistory;
32
33use solana_svm::transaction_processing_callback::TransactionProcessingCallback;
34use solana_svm::transaction_processor::TransactionBatchProcessor;
35
36use solana_svm_callback::InvokeContextCallback;
37#[cfg(feature = "syscall-v2")]
38use trident_syscall_stubs_v2::set_stubs_v2;
39
40use crate::accounts_database::accounts_db::AccountsDB;
41use crate::builder::TridentSVMBuilder;
42
43use crate::trident_fork_graphs::TridentForkGraph;
44use crate::utils;
45use solana_builtins::BUILTINS;
46
47use solana_program_runtime::execution_budget::SVMTransactionExecutionBudget;
48
49use crate::types::trident_program::TridentProgram;
50use crate::utils::get_current_timestamp;
51
52pub struct TridentSVM {
53    pub(crate) accounts: AccountsDB,
54    pub(crate) payer: Keypair,
55    pub(crate) feature_set: Arc<SVMFeatureSet>,
56    pub(crate) processor: TransactionBatchProcessor<TridentForkGraph>,
57    pub(crate) fork_graph: Arc<RwLock<TridentForkGraph>>,
58}
59
60impl TridentSVM {
61    #[cfg(feature = "syscall-v2")]
62    pub(crate) fn initialize_syscalls_v2(&mut self) {
63        set_stubs_v2();
64    }
65}
66
67impl InvokeContextCallback for TridentSVM {}
68
69impl TransactionProcessingCallback for TridentSVM {
70    fn account_matches_owners(&self, account: &Pubkey, owners: &[Pubkey]) -> Option<usize> {
71        self.get_account_shared_data(account)
72            .and_then(|account| owners.iter().position(|key| account.owner().eq(key)))
73    }
74
75    fn get_account_shared_data(&self, pubkey: &Pubkey) -> Option<AccountSharedData> {
76        self.accounts.get_account(pubkey, false)
77    }
78}
79
80impl Default for TridentSVM {
81    fn default() -> Self {
82        let payer = Keypair::new();
83
84        let mut client = Self {
85            accounts: Default::default(),
86            payer: payer.insecure_clone(),
87            feature_set: Arc::new(SVMFeatureSet::default()),
88            processor: TransactionBatchProcessor::<TridentForkGraph>::new(
89                1,
90                1,
91                Arc::downgrade(&Arc::new(RwLock::new(TridentForkGraph {}))),
92                None,
93                None,
94            ),
95            fork_graph: Arc::new(RwLock::new(TridentForkGraph {})),
96        };
97
98        let payer_account = AccountSharedData::new(
99            500_000_000 * 1_000_000_000,
100            0,
101            &solana_sdk_ids::system_program::id(),
102        );
103        client
104            .accounts
105            .set_permanent_account(&payer.pubkey(), &payer_account);
106
107        client
108            .with_processor()
109            .with_sysvars()
110            .with_builtins()
111            .with_solana_program_library()
112    }
113}
114
115impl TridentSVM {
116    pub fn builder() -> TridentSVMBuilder {
117        TridentSVMBuilder::new()
118    }
119    fn with_processor(self) -> Self {
120        {
121            let compute_budget = SVMTransactionExecutionBudget::default();
122
123            let mut cache: std::sync::RwLockWriteGuard<
124                '_,
125                solana_program_runtime::loaded_programs::ProgramCache<TridentForkGraph>,
126            > = self
127                .processor
128                .program_cache
129                .write()
130                .expect("Failed to write to program cache");
131
132            cache.fork_graph = Some(Arc::downgrade(&self.fork_graph));
133
134            cache.environments.program_runtime_v1 = Arc::new(
135                create_program_runtime_environment_v1(
136                    &self.feature_set,
137                    &compute_budget,
138                    false,
139                    false,
140                )
141                .expect("Failed to create program runtime environment"),
142            );
143            cache.environments.program_runtime_v2 =
144                Arc::new(create_program_runtime_environment_v2(&compute_budget, true));
145        }
146
147        self
148    }
149
150    fn with_sysvars(mut self) -> Self {
151        let clock = Clock {
152            unix_timestamp: get_current_timestamp() as i64,
153            ..Default::default()
154        };
155        self.set_sysvar(&clock);
156        self.set_sysvar(&EpochRewards::default());
157        self.set_sysvar(&EpochSchedule::default());
158        #[allow(deprecated)]
159        let fees = Fees::default();
160        self.set_sysvar(&fees);
161        let latest_blockhash = Hash::default();
162        #[allow(deprecated)]
163        self.set_sysvar(&RecentBlockhashes::from_iter([IterItem(
164            0,
165            &latest_blockhash,
166            fees.fee_calculator.lamports_per_signature,
167        )]));
168        self.set_sysvar(&Rent::default());
169        self.set_sysvar(&SlotHashes::new(&[(0, latest_blockhash)]));
170        self.set_sysvar(&SlotHistory::default());
171        self.set_sysvar(&StakeHistory::default());
172
173        self
174    }
175    fn with_builtins(mut self) -> Self {
176        BUILTINS.iter().for_each(|builtint| {
177            self.accounts.set_program(
178                &builtint.program_id,
179                &utils::create_loadable_account_for_test(builtint.name),
180            );
181
182            self.processor.add_builtin(
183                &self,
184                builtint.program_id,
185                builtint.name,
186                ProgramCacheEntry::new_builtin(0, builtint.name.len(), builtint.entrypoint),
187            );
188        });
189
190        self
191    }
192    fn with_solana_program_library(mut self) -> Self {
193        let spl_token = TridentProgram::new(
194            pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"),
195            None,
196            include_bytes!("solana-program-library/spl-token-mainnet.so").to_vec(),
197        );
198
199        self.deploy_binary_program(&spl_token);
200
201        // SPL Token 2022 added for new Token 2022 Trident features
202        let spl_token_2022 = TridentProgram::new(
203            pubkey!("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"),
204            None,
205            include_bytes!("solana-program-library/spl-2022-token-mainnet.so").to_vec(),
206        );
207
208        self.deploy_binary_program(&spl_token_2022);
209
210        let associated_token_program = TridentProgram::new(
211            pubkey!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"),
212            None,
213            include_bytes!("solana-program-library/associated-token-program-mainnet.so").to_vec(),
214        );
215
216        self.deploy_binary_program(&associated_token_program);
217
218        let metaplex_token_metadata = TridentProgram::new(
219            pubkey!("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"),
220            None,
221            include_bytes!("solana-program-library/metaplex-token-metadata.so").to_vec(),
222        );
223
224        self.deploy_binary_program(&metaplex_token_metadata);
225
226        // Interesting to have an Oracle program for testing programs with Price feed manipulation
227        // Another good program would be Pyth Oracle, which is good for cross-chain price feeds
228        let chainlink_oracle = TridentProgram::new(
229            pubkey!("HEvSKofvBgfaexv23kMabbYqxasxU3mQ4ibBMEmJWHny"),
230            None,
231            include_bytes!("solana-program-library/chainlink-oracle.so").to_vec(),
232        );
233
234        self.deploy_binary_program(&chainlink_oracle);
235
236        // Native Stake Pool (SPL Stake Pool):
237        // This program is used for managing stake pools, which can be useful for testing programs that interact with staking
238        let spl_stake_pool = TridentProgram::new(
239            pubkey!("SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy"),
240            None,
241            include_bytes!("solana-program-library/spl-stake-pool.so").to_vec(),
242        );
243        self.deploy_binary_program(&spl_stake_pool);
244
245        // Could be interesting to have candy machine for testing programs that interact with it Minting NFTs
246        let metaplex_candy_machine_v3 = TridentProgram::new(
247            pubkey!("CndyV3LdqHUfDLmE5naZjVN8rBZz4tqhdefbAnjHG3JR"),
248            None,
249            include_bytes!("solana-program-library/metaplex-candy-machine-v3.so").to_vec(),
250        );
251        self.deploy_binary_program(&metaplex_candy_machine_v3);
252
253        self
254    }
255
256    pub fn clear_accounts(&mut self) {
257        self.accounts.reset_temp();
258    }
259}