light_program_test/program_test/
rpc.rs

1use std::{fmt::Debug, marker::Send};
2
3use anchor_lang::pubkey;
4use async_trait::async_trait;
5use borsh::BorshDeserialize;
6use light_client::{
7    indexer::{Indexer, TreeInfo},
8    rpc::{LightClientConfig, Rpc, RpcError},
9};
10use light_compressed_account::{
11    indexer_event::{
12        error::ParseIndexerEventError,
13        event::{BatchPublicTransactionEvent, PublicTransactionEvent},
14        parse::event_from_light_transaction,
15    },
16    TreeType,
17};
18use solana_rpc_client_api::config::RpcSendTransactionConfig;
19use solana_sdk::{
20    account::Account,
21    clock::{Clock, Slot},
22    hash::Hash,
23    instruction::Instruction,
24    pubkey::Pubkey,
25    rent::Rent,
26    signature::{Keypair, Signature},
27    transaction::Transaction,
28};
29use solana_transaction_status_client_types::TransactionStatus;
30
31use crate::{
32    indexer::{TestIndexer, TestIndexerExtensions},
33    program_test::LightProgramTest,
34};
35
36#[async_trait]
37impl Rpc for LightProgramTest {
38    async fn new(_config: LightClientConfig) -> Result<Self, RpcError>
39    where
40        Self: Sized,
41    {
42        Err(RpcError::CustomError(
43            "LightProgramTest::new is not supported in program-test context".into(),
44        ))
45    }
46
47    fn get_payer(&self) -> &Keypair {
48        &self.payer
49    }
50
51    fn get_url(&self) -> String {
52        "get_url doesn't make sense for LightProgramTest".to_string()
53    }
54
55    async fn health(&self) -> Result<(), RpcError> {
56        Ok(())
57    }
58
59    async fn get_program_accounts(
60        &self,
61        _program_id: &Pubkey,
62    ) -> Result<Vec<(Pubkey, Account)>, RpcError> {
63        unimplemented!("get_program_accounts")
64    }
65
66    async fn confirm_transaction(&self, _transaction: Signature) -> Result<bool, RpcError> {
67        Ok(true)
68    }
69
70    async fn get_account(&self, address: Pubkey) -> Result<Option<Account>, RpcError> {
71        Ok(self.context.get_account(&address))
72    }
73
74    async fn get_minimum_balance_for_rent_exemption(
75        &self,
76        data_len: usize,
77    ) -> Result<u64, RpcError> {
78        let rent = self.context.get_sysvar::<Rent>();
79
80        Ok(rent.minimum_balance(data_len))
81    }
82
83    async fn airdrop_lamports(
84        &mut self,
85        to: &Pubkey,
86        lamports: u64,
87    ) -> Result<Signature, RpcError> {
88        let res = self.context.airdrop(to, lamports).map_err(|e| e.err)?;
89        Ok(res.signature)
90    }
91
92    async fn get_balance(&self, pubkey: &Pubkey) -> Result<u64, RpcError> {
93        Ok(self.context.get_balance(pubkey).unwrap())
94    }
95
96    async fn get_latest_blockhash(&mut self) -> Result<(Hash, u64), RpcError> {
97        let slot = self.get_slot().await?;
98        let hash = self.context.latest_blockhash();
99        Ok((hash, slot))
100    }
101
102    async fn get_slot(&self) -> Result<u64, RpcError> {
103        Ok(self.context.get_sysvar::<Clock>().slot)
104    }
105
106    async fn get_transaction_slot(&self, _signature: &Signature) -> Result<u64, RpcError> {
107        unimplemented!();
108    }
109
110    async fn get_signature_statuses(
111        &self,
112        _signatures: &[Signature],
113    ) -> Result<Vec<Option<TransactionStatus>>, RpcError> {
114        Err(RpcError::CustomError(
115            "get_signature_statuses is unimplemented for LightProgramTest".to_string(),
116        ))
117    }
118
119    async fn send_transaction(&self, _transaction: &Transaction) -> Result<Signature, RpcError> {
120        Err(RpcError::CustomError(
121            "send_transaction is unimplemented for ProgramTestConnection".to_string(),
122        ))
123    }
124
125    async fn send_transaction_with_config(
126        &self,
127        _transaction: &Transaction,
128        _config: RpcSendTransactionConfig,
129    ) -> Result<Signature, RpcError> {
130        Err(RpcError::CustomError(
131            "send_transaction_with_config is unimplemented for ProgramTestConnection".to_string(),
132        ))
133    }
134
135    async fn process_transaction(
136        &mut self,
137        transaction: Transaction,
138    ) -> Result<Signature, RpcError> {
139        let sig = *transaction.signatures.first().unwrap();
140        if self.indexer.is_some() {
141            self._send_transaction_with_batched_event(transaction)
142                .await?;
143        } else {
144            let _res = self.context.send_transaction(transaction).map_err(|x| {
145                if self.config.log_failed_tx {
146                    println!("{}", x.meta.pretty_logs());
147                }
148
149                RpcError::TransactionError(x.err)
150            })?;
151            self.maybe_print_logs(_res.pretty_logs());
152        }
153        Ok(sig)
154    }
155
156    async fn process_transaction_with_context(
157        &mut self,
158        transaction: Transaction,
159    ) -> Result<(Signature, Slot), RpcError> {
160        let sig = *transaction.signatures.first().unwrap();
161        let _res = self.context.send_transaction(transaction).map_err(|x| {
162            if self.config.log_failed_tx {
163                println!("{}", x.meta.pretty_logs());
164            }
165            RpcError::TransactionError(x.err)
166        })?;
167        self.maybe_print_logs(_res.pretty_logs());
168
169        let slot = self.context.get_sysvar::<Clock>().slot;
170        Ok((sig, slot))
171    }
172
173    async fn create_and_send_transaction_with_event<T>(
174        &mut self,
175        instructions: &[Instruction],
176        payer: &Pubkey,
177        signers: &[&Keypair],
178    ) -> Result<Option<(T, Signature, u64)>, RpcError>
179    where
180        T: BorshDeserialize + Send + Debug,
181    {
182        self._create_and_send_transaction_with_event::<T>(instructions, payer, signers)
183            .await
184    }
185
186    async fn create_and_send_transaction_with_batched_event(
187        &mut self,
188        instructions: &[Instruction],
189        payer: &Pubkey,
190        signers: &[&Keypair],
191    ) -> Result<Option<(Vec<BatchPublicTransactionEvent>, Signature, Slot)>, RpcError> {
192        self._create_and_send_transaction_with_batched_event(instructions, payer, signers)
193            .await
194    }
195
196    async fn create_and_send_transaction_with_public_event(
197        &mut self,
198        instruction: &[Instruction],
199        payer: &Pubkey,
200        signers: &[&Keypair],
201    ) -> Result<Option<(PublicTransactionEvent, Signature, Slot)>, RpcError> {
202        let event = self
203            ._create_and_send_transaction_with_batched_event(instruction, payer, signers)
204            .await?;
205        let event = event.map(|e| (e.0[0].event.clone(), e.1, e.2));
206
207        Ok(event)
208    }
209
210    fn indexer(&self) -> Result<&impl Indexer, RpcError> {
211        self.indexer.as_ref().ok_or(RpcError::IndexerNotInitialized)
212    }
213
214    fn indexer_mut(&mut self) -> Result<&mut impl Indexer, RpcError> {
215        self.indexer.as_mut().ok_or(RpcError::IndexerNotInitialized)
216    }
217
218    /// Fetch the latest state tree addresses from the cluster.
219    async fn get_latest_active_state_trees(&mut self) -> Result<Vec<TreeInfo>, RpcError> {
220        #[cfg(not(feature = "v2"))]
221        return Ok(self
222            .test_accounts
223            .v1_state_trees
224            .to_vec()
225            .into_iter()
226            .map(|tree| tree.into())
227            .collect());
228        #[cfg(feature = "v2")]
229        return Ok(self
230            .test_accounts
231            .v2_state_trees
232            .iter()
233            .map(|tree| (*tree).into())
234            .collect());
235    }
236
237    /// Fetch the latest state tree addresses from the cluster.
238    fn get_state_tree_infos(&self) -> Vec<TreeInfo> {
239        #[cfg(not(feature = "v2"))]
240        return self
241            .test_accounts
242            .v1_state_trees
243            .to_vec()
244            .into_iter()
245            .map(|tree| tree.into())
246            .collect();
247        #[cfg(feature = "v2")]
248        return self
249            .test_accounts
250            .v2_state_trees
251            .iter()
252            .map(|tree| (*tree).into())
253            .collect();
254    }
255
256    /// Gets a random active state tree.
257    /// State trees are cached and have to be fetched or set.
258    fn get_random_state_tree_info(&self) -> Result<TreeInfo, RpcError> {
259        use rand::Rng;
260        let mut rng = rand::thread_rng();
261        #[cfg(not(feature = "v2"))]
262        {
263            if self.test_accounts.v1_state_trees.is_empty() {
264                return Err(RpcError::NoStateTreesAvailable);
265            }
266            Ok(self.test_accounts.v1_state_trees
267                [rng.gen_range(0..self.test_accounts.v1_state_trees.len())]
268            .into())
269        }
270        #[cfg(feature = "v2")]
271        {
272            if self.test_accounts.v2_state_trees.is_empty() {
273                return Err(RpcError::NoStateTreesAvailable);
274            }
275            Ok(self.test_accounts.v2_state_trees
276                [rng.gen_range(0..self.test_accounts.v2_state_trees.len())]
277            .into())
278        }
279    }
280
281    fn get_address_tree_v1(&self) -> TreeInfo {
282        TreeInfo {
283            tree: pubkey!("amt1Ayt45jfbdw5YSo7iz6WZxUmnZsQTYXy82hVwyC2"),
284            queue: pubkey!("aq1S9z4reTSQAdgWHGD2zDaS39sjGrAxbR31vxJ2F4F"),
285            cpi_context: None,
286            next_tree_info: None,
287            tree_type: TreeType::AddressV1,
288        }
289    }
290}
291
292impl LightProgramTest {
293    fn maybe_print_logs(&self, logs: impl std::fmt::Display) {
294        if !self.config.no_logs && cfg!(debug_assertions) && std::env::var("RUST_BACKTRACE").is_ok()
295        {
296            println!("{}", logs);
297        }
298    }
299    #[cfg(feature = "v2")]
300    pub fn get_address_tree_v2(&self) -> TreeInfo {
301        TreeInfo {
302            tree: pubkey!("EzKE84aVTkCUhDHLELqyJaq1Y7UVVmqxXqZjVHwHY3rK"),
303            queue: pubkey!("EzKE84aVTkCUhDHLELqyJaq1Y7UVVmqxXqZjVHwHY3rK"),
304            cpi_context: None,
305            next_tree_info: None,
306            tree_type: TreeType::AddressV2,
307        }
308    }
309
310    async fn _send_transaction_with_batched_event(
311        &mut self,
312        transaction: Transaction,
313    ) -> Result<Option<(Vec<BatchPublicTransactionEvent>, Signature, Slot)>, RpcError> {
314        let mut vec = Vec::new();
315
316        let signature = transaction.signatures[0];
317        // Simulate the transaction. Currently, in banks-client/server, only
318        // simulations are able to track CPIs. Therefore, simulating is the
319        // only way to retrieve the event.
320        let simulation_result = self
321            .context
322            .simulate_transaction(transaction.clone())
323            .map_err(|x| {
324                if self.config.log_failed_tx {
325                    println!("{}", x.meta.pretty_logs());
326                }
327
328                RpcError::TransactionError(x.err)
329            })?;
330
331        // Try old event deserialization.
332        let event = simulation_result
333            .meta
334            .inner_instructions
335            .iter()
336            .flatten()
337            .find_map(|inner_instruction| {
338                PublicTransactionEvent::try_from_slice(&inner_instruction.instruction.data).ok()
339            });
340        let event = if let Some(event) = event {
341            Some(vec![BatchPublicTransactionEvent {
342                event,
343                ..Default::default()
344            }])
345        } else {
346            // If PublicTransactionEvent wasn't successful deserialize new event.
347            let mut vec_accounts = Vec::<Vec<Pubkey>>::new();
348            let mut program_ids = Vec::new();
349
350            transaction.message.instructions.iter().for_each(|i| {
351                program_ids.push(transaction.message.account_keys[i.program_id_index as usize]);
352                vec.push(i.data.clone());
353                vec_accounts.push(
354                    i.accounts
355                        .iter()
356                        .map(|x| transaction.message.account_keys[*x as usize])
357                        .collect(),
358                );
359            });
360            simulation_result
361                .meta
362                .inner_instructions
363                .iter()
364                .flatten()
365                .find_map(|inner_instruction| {
366                    vec.push(inner_instruction.instruction.data.clone());
367                    program_ids.push(
368                        transaction.message.account_keys
369                            [inner_instruction.instruction.program_id_index as usize],
370                    );
371                    vec_accounts.push(
372                        inner_instruction
373                            .instruction
374                            .accounts
375                            .iter()
376                            .map(|x| transaction.message.account_keys[*x as usize])
377                            .collect(),
378                    );
379                    None::<PublicTransactionEvent>
380                });
381
382            event_from_light_transaction(
383                &program_ids.iter().map(|x| (*x).into()).collect::<Vec<_>>(),
384                vec.as_slice(),
385                vec_accounts
386                    .iter()
387                    .map(|inner_vec| inner_vec.iter().map(|x| (*x).into()).collect())
388                    .collect(),
389            )
390            .or(Ok::<
391                Option<Vec<BatchPublicTransactionEvent>>,
392                ParseIndexerEventError,
393            >(None))?
394        };
395
396        // Transaction was successful, execute it.
397        let _res = self.context.send_transaction(transaction).map_err(|x| {
398            // Prevent duplicate prints for failing tx.
399
400            if self.config.log_failed_tx {
401                println!("{}", x.meta.pretty_logs());
402            }
403
404            RpcError::TransactionError(x.err)
405        })?;
406        if !self.config.no_logs {
407            #[cfg(debug_assertions)]
408            {
409                if std::env::var("RUST_BACKTRACE").is_ok() {
410                    // Print all tx logs and events.
411                    println!("{}", _res.pretty_logs());
412                    println!("event:\n {:?}", event);
413                }
414            }
415        }
416
417        let slot = self.context.get_sysvar::<Clock>().slot;
418        let event = event.map(|e| (e, signature, slot));
419
420        if let Some(indexer) = self.indexer.as_mut() {
421            if let Some(events) = event.as_ref() {
422                for event in events.0.iter() {
423                    <TestIndexer as TestIndexerExtensions>::add_compressed_accounts_with_token_data(
424                        indexer,
425                        slot,
426                        &event.event,
427                    );
428                }
429            }
430        }
431
432        Ok(event)
433    }
434
435    async fn _create_and_send_transaction_with_event<T>(
436        &mut self,
437        instruction: &[Instruction],
438        payer: &Pubkey,
439        signers: &[&Keypair],
440    ) -> Result<Option<(T, Signature, Slot)>, RpcError>
441    where
442        T: BorshDeserialize + Send + Debug,
443    {
444        let transaction = Transaction::new_signed_with_payer(
445            instruction,
446            Some(payer),
447            signers,
448            self.context.latest_blockhash(),
449        );
450
451        let signature = transaction.signatures[0];
452        // Simulate the transaction. Currently, in banks-client/server, only
453        // simulations are able to track CPIs. Therefore, simulating is the
454        // only way to retrieve the event.
455        let simulation_result = self
456            .context
457            .simulate_transaction(transaction.clone())
458            .map_err(|x| RpcError::from(x.err))?;
459
460        let event = simulation_result
461            .meta
462            .inner_instructions
463            .iter()
464            .flatten()
465            .find_map(|inner_instruction| {
466                T::try_from_slice(&inner_instruction.instruction.data).ok()
467            });
468        // If transaction was successful, execute it.
469        let _res = self.context.send_transaction(transaction).map_err(|x| {
470            if self.config.log_failed_tx {
471                println!("{}", x.meta.pretty_logs());
472            }
473            RpcError::TransactionError(x.err)
474        })?;
475        self.maybe_print_logs(_res.pretty_logs());
476
477        let slot = self.get_slot().await?;
478        let result = event.map(|event| (event, signature, slot));
479        Ok(result)
480    }
481
482    async fn _create_and_send_transaction_with_batched_event(
483        &mut self,
484        instruction: &[Instruction],
485        payer: &Pubkey,
486        signers: &[&Keypair],
487    ) -> Result<Option<(Vec<BatchPublicTransactionEvent>, Signature, Slot)>, RpcError> {
488        let transaction = Transaction::new_signed_with_payer(
489            instruction,
490            Some(payer),
491            signers,
492            self.context.latest_blockhash(),
493        );
494
495        self._send_transaction_with_batched_event(transaction).await
496    }
497}