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 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 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 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 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 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 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 let _res = self.context.send_transaction(transaction).map_err(|x| {
398 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 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 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 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}