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::TreeType;
11use light_event::{
12 error::ParseIndexerEventError,
13 event::{BatchPublicTransactionEvent, PublicTransactionEvent},
14 parse::event_from_light_transaction,
15};
16use solana_rpc_client_api::config::RpcSendTransactionConfig;
17use solana_sdk::{
18 account::Account,
19 clock::{Clock, Slot},
20 hash::Hash,
21 instruction::Instruction,
22 pubkey::Pubkey,
23 rent::Rent,
24 signature::{Keypair, Signature},
25 transaction::Transaction,
26};
27use solana_transaction_status_client_types::TransactionStatus;
28
29use crate::{
30 indexer::{TestIndexer, TestIndexerExtensions},
31 program_test::LightProgramTest,
32};
33
34#[async_trait]
35impl Rpc for LightProgramTest {
36 async fn new(_config: LightClientConfig) -> Result<Self, RpcError>
37 where
38 Self: Sized,
39 {
40 Err(RpcError::CustomError(
41 "LightProgramTest::new is not supported in program-test context".into(),
42 ))
43 }
44
45 fn get_payer(&self) -> &Keypair {
46 &self.payer
47 }
48
49 fn get_url(&self) -> String {
50 "get_url doesn't make sense for LightProgramTest".to_string()
51 }
52
53 async fn health(&self) -> Result<(), RpcError> {
54 Ok(())
55 }
56
57 async fn get_program_accounts(
58 &self,
59 _program_id: &Pubkey,
60 ) -> Result<Vec<(Pubkey, Account)>, RpcError> {
61 unimplemented!("get_program_accounts")
62 }
63
64 async fn confirm_transaction(&self, _transaction: Signature) -> Result<bool, RpcError> {
65 Ok(true)
66 }
67
68 async fn get_account(&self, address: Pubkey) -> Result<Option<Account>, RpcError> {
69 Ok(self.context.get_account(&address))
70 }
71
72 async fn get_minimum_balance_for_rent_exemption(
73 &self,
74 data_len: usize,
75 ) -> Result<u64, RpcError> {
76 let rent = self.context.get_sysvar::<Rent>();
77
78 Ok(rent.minimum_balance(data_len))
79 }
80
81 async fn airdrop_lamports(
82 &mut self,
83 to: &Pubkey,
84 lamports: u64,
85 ) -> Result<Signature, RpcError> {
86 let res = self.context.airdrop(to, lamports).map_err(|e| e.err)?;
87 Ok(res.signature)
88 }
89
90 async fn get_balance(&self, pubkey: &Pubkey) -> Result<u64, RpcError> {
91 Ok(self.context.get_balance(pubkey).unwrap())
92 }
93
94 async fn get_latest_blockhash(&mut self) -> Result<(Hash, u64), RpcError> {
95 let slot = self.get_slot().await?;
96 let hash = self.context.latest_blockhash();
97 Ok((hash, slot))
98 }
99
100 async fn get_slot(&self) -> Result<u64, RpcError> {
101 Ok(self.context.get_sysvar::<Clock>().slot)
102 }
103
104 async fn get_transaction_slot(&self, _signature: &Signature) -> Result<u64, RpcError> {
105 unimplemented!();
106 }
107
108 async fn get_signature_statuses(
109 &self,
110 _signatures: &[Signature],
111 ) -> Result<Vec<Option<TransactionStatus>>, RpcError> {
112 Err(RpcError::CustomError(
113 "get_signature_statuses is unimplemented for LightProgramTest".to_string(),
114 ))
115 }
116
117 async fn send_transaction(&self, _transaction: &Transaction) -> Result<Signature, RpcError> {
118 Err(RpcError::CustomError(
119 "send_transaction is unimplemented for ProgramTestConnection".to_string(),
120 ))
121 }
122
123 async fn send_transaction_with_config(
124 &self,
125 _transaction: &Transaction,
126 _config: RpcSendTransactionConfig,
127 ) -> Result<Signature, RpcError> {
128 Err(RpcError::CustomError(
129 "send_transaction_with_config is unimplemented for ProgramTestConnection".to_string(),
130 ))
131 }
132
133 async fn process_transaction(
134 &mut self,
135 transaction: Transaction,
136 ) -> Result<Signature, RpcError> {
137 let sig = *transaction.signatures.first().unwrap();
138 if self.indexer.is_some() {
139 self._send_transaction_with_batched_event(transaction)
141 .await?;
142 } else {
143 let pre_context_snapshot = self.context.clone();
145
146 self.transaction_counter += 1;
148 let _res = self.context.send_transaction(transaction).map_err(|x| {
149 if self.config.log_failed_tx {
150 println!("{}", x.meta.pretty_logs());
151 }
152
153 RpcError::TransactionError(x.err)
154 })?;
155
156 self.maybe_print_logs(_res.pretty_logs());
157
158 self.pre_context = Some(pre_context_snapshot);
160 }
161 Ok(sig)
162 }
163
164 async fn process_transaction_with_context(
165 &mut self,
166 transaction: Transaction,
167 ) -> Result<(Signature, Slot), RpcError> {
168 let sig = *transaction.signatures.first().unwrap();
169
170 let pre_context_snapshot = self.context.clone();
172
173 self.transaction_counter += 1;
174 let _res = self.context.send_transaction(transaction).map_err(|x| {
175 if self.config.log_failed_tx {
176 println!("{}", x.meta.pretty_logs());
177 }
178 RpcError::TransactionError(x.err)
179 })?;
180
181 let slot = self.context.get_sysvar::<Clock>().slot;
182 self.maybe_print_logs(_res.pretty_logs());
183
184 self.pre_context = Some(pre_context_snapshot);
186
187 Ok((sig, slot))
188 }
189
190 async fn create_and_send_transaction_with_event<T>(
191 &mut self,
192 instructions: &[Instruction],
193 payer: &Pubkey,
194 signers: &[&Keypair],
195 ) -> Result<Option<(T, Signature, u64)>, RpcError>
196 where
197 T: BorshDeserialize + Send + Debug,
198 {
199 self._create_and_send_transaction_with_event::<T>(instructions, payer, signers)
200 .await
201 }
202
203 async fn create_and_send_transaction_with_batched_event(
204 &mut self,
205 instructions: &[Instruction],
206 payer: &Pubkey,
207 signers: &[&Keypair],
208 ) -> Result<Option<(Vec<BatchPublicTransactionEvent>, Signature, Slot)>, RpcError> {
209 self._create_and_send_transaction_with_batched_event(instructions, payer, signers)
210 .await
211 }
212
213 async fn create_and_send_transaction_with_public_event(
214 &mut self,
215 instruction: &[Instruction],
216 payer: &Pubkey,
217 signers: &[&Keypair],
218 ) -> Result<Option<(PublicTransactionEvent, Signature, Slot)>, RpcError> {
219 let event = self
220 ._create_and_send_transaction_with_batched_event(instruction, payer, signers)
221 .await?;
222 let event = event.map(|e| (e.0[0].event.clone(), e.1, e.2));
223
224 Ok(event)
225 }
226
227 fn indexer(&self) -> Result<&impl Indexer, RpcError> {
228 self.indexer.as_ref().ok_or(RpcError::IndexerNotInitialized)
229 }
230
231 fn indexer_mut(&mut self) -> Result<&mut impl Indexer, RpcError> {
232 self.indexer.as_mut().ok_or(RpcError::IndexerNotInitialized)
233 }
234
235 async fn get_latest_active_state_trees(&mut self) -> Result<Vec<TreeInfo>, RpcError> {
237 #[cfg(not(feature = "v2"))]
238 return Ok(self
239 .test_accounts
240 .v1_state_trees
241 .iter()
242 .copied()
243 .map(|tree| tree.into())
244 .collect());
245 #[cfg(feature = "v2")]
246 return Ok(self
247 .test_accounts
248 .v2_state_trees
249 .iter()
250 .map(|tree| (*tree).into())
251 .collect());
252 }
253
254 fn get_state_tree_infos(&self) -> Vec<TreeInfo> {
256 #[cfg(not(feature = "v2"))]
257 return self
258 .test_accounts
259 .v1_state_trees
260 .iter()
261 .copied()
262 .map(|tree| tree.into())
263 .collect();
264 #[cfg(feature = "v2")]
265 return self
266 .test_accounts
267 .v2_state_trees
268 .iter()
269 .map(|tree| (*tree).into())
270 .collect();
271 }
272
273 fn get_random_state_tree_info(&self) -> Result<TreeInfo, RpcError> {
276 use rand::Rng;
277 let mut rng = rand::thread_rng();
278 #[cfg(not(feature = "v2"))]
279 {
280 if self.test_accounts.v1_state_trees.is_empty() {
281 return Err(RpcError::NoStateTreesAvailable);
282 }
283 Ok(self.test_accounts.v1_state_trees
284 [rng.gen_range(0..self.test_accounts.v1_state_trees.len())]
285 .into())
286 }
287 #[cfg(feature = "v2")]
288 {
289 if self.test_accounts.v2_state_trees.is_empty() {
290 return Err(RpcError::NoStateTreesAvailable);
291 }
292 Ok(self.test_accounts.v2_state_trees
293 [rng.gen_range(0..self.test_accounts.v2_state_trees.len())]
294 .into())
295 }
296 }
297
298 fn get_random_state_tree_info_v1(&self) -> Result<TreeInfo, RpcError> {
301 use rand::Rng;
302 let mut rng = rand::thread_rng();
303 if self.test_accounts.v1_state_trees.is_empty() {
304 return Err(RpcError::NoStateTreesAvailable);
305 }
306 Ok(self.test_accounts.v1_state_trees
307 [rng.gen_range(0..self.test_accounts.v1_state_trees.len())]
308 .into())
309 }
310
311 fn get_address_tree_v1(&self) -> TreeInfo {
312 TreeInfo {
313 tree: pubkey!("amt1Ayt45jfbdw5YSo7iz6WZxUmnZsQTYXy82hVwyC2"),
314 queue: pubkey!("aq1S9z4reTSQAdgWHGD2zDaS39sjGrAxbR31vxJ2F4F"),
315 cpi_context: None,
316 next_tree_info: None,
317 tree_type: TreeType::AddressV1,
318 }
319 }
320
321 fn get_address_tree_v2(&self) -> TreeInfo {
322 TreeInfo {
323 tree: pubkey!("amt2kaJA14v3urZbZvnc5v2np8jqvc4Z8zDep5wbtzx"),
324 queue: pubkey!("amt2kaJA14v3urZbZvnc5v2np8jqvc4Z8zDep5wbtzx"),
325 cpi_context: None,
326 next_tree_info: None,
327 tree_type: TreeType::AddressV2,
328 }
329 }
330}
331
332impl LightProgramTest {
333 fn maybe_print_logs(&self, logs: impl std::fmt::Display) {
334 if crate::logging::should_use_enhanced_logging(&self.config) {
336 return;
338 }
339
340 if !self.config.no_logs && cfg!(debug_assertions) && std::env::var("RUST_BACKTRACE").is_ok()
342 {
343 println!("{}", logs);
344 }
345 }
346
347 async fn _send_transaction_with_batched_event(
348 &mut self,
349 transaction: Transaction,
350 ) -> Result<Option<(Vec<BatchPublicTransactionEvent>, Signature, Slot)>, RpcError> {
351 let mut vec = Vec::new();
352
353 let signature = transaction.signatures[0];
354 let transaction_for_logging = transaction.clone(); let pre_context_snapshot = self.context.clone();
358
359 let simulation_result = self.context.simulate_transaction(transaction.clone());
363
364 self.transaction_counter += 1;
366 let transaction_result = self.context.send_transaction(transaction.clone());
367 let slot = self.context.get_sysvar::<Clock>().slot;
368
369 if crate::logging::should_use_enhanced_logging(&self.config) {
371 crate::logging::log_transaction_enhanced(
372 &self.config,
373 &transaction_for_logging,
374 &transaction_result,
375 &signature,
376 slot,
377 self.transaction_counter,
378 );
379 }
380
381 let _res = transaction_result.as_ref().map_err(|x| {
383 if self.config.log_failed_tx {
385 crate::logging::log_transaction_enhanced_with_console(
386 &self.config,
387 &transaction_for_logging,
388 &transaction_result,
389 &signature,
390 slot,
391 self.transaction_counter,
392 true, );
394 }
395 RpcError::TransactionError(x.err.clone())
396 })?;
397
398 if !self.config.no_logs && std::env::var("RUST_BACKTRACE").is_ok() {
400 if crate::logging::should_use_enhanced_logging(&self.config) {
401 crate::logging::log_transaction_enhanced_with_console(
403 &self.config,
404 &transaction_for_logging,
405 &transaction_result,
406 &signature,
407 slot,
408 self.transaction_counter,
409 true, );
411
412 } else {
418 self.maybe_print_logs(_res.pretty_logs());
420 }
421 }
422
423 let simulation_result = simulation_result.unwrap();
424 let event = simulation_result
426 .meta
427 .inner_instructions
428 .iter()
429 .flatten()
430 .find_map(|inner_instruction| {
431 PublicTransactionEvent::try_from_slice(&inner_instruction.instruction.data).ok()
432 });
433 let event = if let Some(event) = event {
434 Some(vec![BatchPublicTransactionEvent {
435 event,
436 ..Default::default()
437 }])
438 } else {
439 let mut vec_accounts = Vec::<Vec<Pubkey>>::new();
441 let mut program_ids = Vec::new();
442
443 transaction.message.instructions.iter().for_each(|i| {
444 program_ids.push(transaction.message.account_keys[i.program_id_index as usize]);
445 vec.push(i.data.clone());
446 vec_accounts.push(
447 i.accounts
448 .iter()
449 .map(|x| transaction.message.account_keys[*x as usize])
450 .collect(),
451 );
452 });
453 simulation_result
454 .meta
455 .inner_instructions
456 .iter()
457 .flatten()
458 .find_map(|inner_instruction| {
459 vec.push(inner_instruction.instruction.data.clone());
460 program_ids.push(
461 transaction.message.account_keys
462 [inner_instruction.instruction.program_id_index as usize],
463 );
464 vec_accounts.push(
465 inner_instruction
466 .instruction
467 .accounts
468 .iter()
469 .map(|x| transaction.message.account_keys[*x as usize])
470 .collect(),
471 );
472 None::<PublicTransactionEvent>
473 });
474
475 event_from_light_transaction(
476 &program_ids.iter().map(|x| (*x).into()).collect::<Vec<_>>(),
477 vec.as_slice(),
478 vec_accounts
479 .iter()
480 .map(|inner_vec| inner_vec.iter().map(|x| (*x).into()).collect())
481 .collect(),
482 )
483 .or(Ok::<
484 Option<Vec<BatchPublicTransactionEvent>>,
485 ParseIndexerEventError,
486 >(None))?
487 };
488 if self.config.log_light_protocol_events {
489 println!("event:\n {:?}", event);
490 }
491 let event = event.map(|e| (e, signature, slot));
492
493 if let Some(indexer) = self.indexer.as_mut() {
494 if let Some(events) = event.as_ref() {
495 for event in events.0.iter() {
496 <TestIndexer as TestIndexerExtensions>::add_compressed_accounts_with_token_data(
497 indexer,
498 slot,
499 &event.event,
500 );
501 }
502 }
503 }
504
505 self.pre_context = Some(pre_context_snapshot);
507
508 Ok(event)
509 }
510
511 async fn _create_and_send_transaction_with_event<T>(
512 &mut self,
513 instruction: &[Instruction],
514 payer: &Pubkey,
515 signers: &[&Keypair],
516 ) -> Result<Option<(T, Signature, Slot)>, RpcError>
517 where
518 T: BorshDeserialize + Send + Debug,
519 {
520 let transaction = Transaction::new_signed_with_payer(
521 instruction,
522 Some(payer),
523 signers,
524 self.context.latest_blockhash(),
525 );
526
527 let signature = transaction.signatures[0];
528
529 let pre_context_snapshot = self.context.clone();
531
532 let simulation_result = self
536 .context
537 .simulate_transaction(transaction.clone())
538 .map_err(|x| RpcError::from(x.err))?;
539
540 let event = simulation_result
541 .meta
542 .inner_instructions
543 .iter()
544 .flatten()
545 .find_map(|inner_instruction| {
546 T::try_from_slice(&inner_instruction.instruction.data).ok()
547 });
548 self.transaction_counter += 1;
550 let _res = self.context.send_transaction(transaction).map_err(|x| {
551 if self.config.log_failed_tx {
552 println!("{}", x.meta.pretty_logs());
553 }
554 RpcError::TransactionError(x.err)
555 })?;
556 self.maybe_print_logs(_res.pretty_logs());
557
558 self.pre_context = Some(pre_context_snapshot);
560
561 let slot = self.get_slot().await?;
562 let result = event.map(|event| (event, signature, slot));
563 Ok(result)
564 }
565
566 async fn _create_and_send_transaction_with_batched_event(
567 &mut self,
568 instruction: &[Instruction],
569 payer: &Pubkey,
570 signers: &[&Keypair],
571 ) -> Result<Option<(Vec<BatchPublicTransactionEvent>, Signature, Slot)>, RpcError> {
572 let transaction = Transaction::new_signed_with_payer(
573 instruction,
574 Some(payer),
575 signers,
576 self.context.latest_blockhash(),
577 );
578
579 self._send_transaction_with_batched_event(transaction).await
580 }
581}