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)
143 .await?;
144 } else {
145 self.transaction_counter += 1;
147 let _res = self.context.send_transaction(transaction).map_err(|x| {
148 if self.config.log_failed_tx {
149 println!("{}", x.meta.pretty_logs());
150 }
151
152 RpcError::TransactionError(x.err)
153 })?;
154
155 self.maybe_print_logs(_res.pretty_logs());
156 }
157 Ok(sig)
158 }
159
160 async fn process_transaction_with_context(
161 &mut self,
162 transaction: Transaction,
163 ) -> Result<(Signature, Slot), RpcError> {
164 let sig = *transaction.signatures.first().unwrap();
165 self.transaction_counter += 1;
166 let _res = self.context.send_transaction(transaction).map_err(|x| {
167 if self.config.log_failed_tx {
168 println!("{}", x.meta.pretty_logs());
169 }
170 RpcError::TransactionError(x.err)
171 })?;
172
173 let slot = self.context.get_sysvar::<Clock>().slot;
174 self.maybe_print_logs(_res.pretty_logs());
175
176 Ok((sig, slot))
177 }
178
179 async fn create_and_send_transaction_with_event<T>(
180 &mut self,
181 instructions: &[Instruction],
182 payer: &Pubkey,
183 signers: &[&Keypair],
184 ) -> Result<Option<(T, Signature, u64)>, RpcError>
185 where
186 T: BorshDeserialize + Send + Debug,
187 {
188 self._create_and_send_transaction_with_event::<T>(instructions, payer, signers)
189 .await
190 }
191
192 async fn create_and_send_transaction_with_batched_event(
193 &mut self,
194 instructions: &[Instruction],
195 payer: &Pubkey,
196 signers: &[&Keypair],
197 ) -> Result<Option<(Vec<BatchPublicTransactionEvent>, Signature, Slot)>, RpcError> {
198 self._create_and_send_transaction_with_batched_event(instructions, payer, signers)
199 .await
200 }
201
202 async fn create_and_send_transaction_with_public_event(
203 &mut self,
204 instruction: &[Instruction],
205 payer: &Pubkey,
206 signers: &[&Keypair],
207 ) -> Result<Option<(PublicTransactionEvent, Signature, Slot)>, RpcError> {
208 let event = self
209 ._create_and_send_transaction_with_batched_event(instruction, payer, signers)
210 .await?;
211 let event = event.map(|e| (e.0[0].event.clone(), e.1, e.2));
212
213 Ok(event)
214 }
215
216 fn indexer(&self) -> Result<&impl Indexer, RpcError> {
217 self.indexer.as_ref().ok_or(RpcError::IndexerNotInitialized)
218 }
219
220 fn indexer_mut(&mut self) -> Result<&mut impl Indexer, RpcError> {
221 self.indexer.as_mut().ok_or(RpcError::IndexerNotInitialized)
222 }
223
224 async fn get_latest_active_state_trees(&mut self) -> Result<Vec<TreeInfo>, RpcError> {
226 #[cfg(not(feature = "v2"))]
227 return Ok(self
228 .test_accounts
229 .v1_state_trees
230 .iter()
231 .copied()
232 .map(|tree| tree.into())
233 .collect());
234 #[cfg(feature = "v2")]
235 return Ok(self
236 .test_accounts
237 .v2_state_trees
238 .iter()
239 .map(|tree| (*tree).into())
240 .collect());
241 }
242
243 fn get_state_tree_infos(&self) -> Vec<TreeInfo> {
245 #[cfg(not(feature = "v2"))]
246 return self
247 .test_accounts
248 .v1_state_trees
249 .iter()
250 .copied()
251 .map(|tree| tree.into())
252 .collect();
253 #[cfg(feature = "v2")]
254 return self
255 .test_accounts
256 .v2_state_trees
257 .iter()
258 .map(|tree| (*tree).into())
259 .collect();
260 }
261
262 fn get_random_state_tree_info(&self) -> Result<TreeInfo, RpcError> {
265 use rand::Rng;
266 let mut rng = rand::thread_rng();
267 #[cfg(not(feature = "v2"))]
268 {
269 if self.test_accounts.v1_state_trees.is_empty() {
270 return Err(RpcError::NoStateTreesAvailable);
271 }
272 Ok(self.test_accounts.v1_state_trees
273 [rng.gen_range(0..self.test_accounts.v1_state_trees.len())]
274 .into())
275 }
276 #[cfg(feature = "v2")]
277 {
278 if self.test_accounts.v2_state_trees.is_empty() {
279 return Err(RpcError::NoStateTreesAvailable);
280 }
281 Ok(self.test_accounts.v2_state_trees
282 [rng.gen_range(0..self.test_accounts.v2_state_trees.len())]
283 .into())
284 }
285 }
286
287 fn get_address_tree_v1(&self) -> TreeInfo {
288 TreeInfo {
289 tree: pubkey!("amt1Ayt45jfbdw5YSo7iz6WZxUmnZsQTYXy82hVwyC2"),
290 queue: pubkey!("aq1S9z4reTSQAdgWHGD2zDaS39sjGrAxbR31vxJ2F4F"),
291 cpi_context: None,
292 next_tree_info: None,
293 tree_type: TreeType::AddressV1,
294 }
295 }
296
297 fn get_address_tree_v2(&self) -> TreeInfo {
298 TreeInfo {
299 tree: pubkey!("amt2kaJA14v3urZbZvnc5v2np8jqvc4Z8zDep5wbtzx"),
300 queue: pubkey!("amt2kaJA14v3urZbZvnc5v2np8jqvc4Z8zDep5wbtzx"),
301 cpi_context: None,
302 next_tree_info: None,
303 tree_type: TreeType::AddressV2,
304 }
305 }
306}
307
308impl LightProgramTest {
309 fn maybe_print_logs(&self, logs: impl std::fmt::Display) {
310 if crate::logging::should_use_enhanced_logging(&self.config) {
312 return;
314 }
315
316 if !self.config.no_logs && cfg!(debug_assertions) && std::env::var("RUST_BACKTRACE").is_ok()
318 {
319 println!("{}", logs);
320 }
321 }
322
323 async fn _send_transaction_with_batched_event(
324 &mut self,
325 transaction: Transaction,
326 ) -> Result<Option<(Vec<BatchPublicTransactionEvent>, Signature, Slot)>, RpcError> {
327 let mut vec = Vec::new();
328
329 let signature = transaction.signatures[0];
330 let transaction_for_logging = transaction.clone(); let simulation_result = self.context.simulate_transaction(transaction.clone());
335
336 self.transaction_counter += 1;
338 let transaction_result = self.context.send_transaction(transaction.clone());
339 let slot = self.context.get_sysvar::<Clock>().slot;
340
341 if crate::logging::should_use_enhanced_logging(&self.config) {
343 crate::logging::log_transaction_enhanced(
344 &self.config,
345 &transaction_for_logging,
346 &transaction_result,
347 &signature,
348 slot,
349 self.transaction_counter,
350 );
351 }
352
353 let _res = transaction_result.as_ref().map_err(|x| {
355 if self.config.log_failed_tx {
357 crate::logging::log_transaction_enhanced_with_console(
358 &self.config,
359 &transaction_for_logging,
360 &transaction_result,
361 &signature,
362 slot,
363 self.transaction_counter,
364 true, );
366 }
367 RpcError::TransactionError(x.err.clone())
368 })?;
369
370 if !self.config.no_logs && std::env::var("RUST_BACKTRACE").is_ok() {
372 if crate::logging::should_use_enhanced_logging(&self.config) {
373 crate::logging::log_transaction_enhanced_with_console(
375 &self.config,
376 &transaction_for_logging,
377 &transaction_result,
378 &signature,
379 slot,
380 self.transaction_counter,
381 true, );
383
384 } else {
390 self.maybe_print_logs(_res.pretty_logs());
392 }
393 }
394
395 let simulation_result = simulation_result.unwrap();
396 let event = simulation_result
398 .meta
399 .inner_instructions
400 .iter()
401 .flatten()
402 .find_map(|inner_instruction| {
403 PublicTransactionEvent::try_from_slice(&inner_instruction.instruction.data).ok()
404 });
405 let event = if let Some(event) = event {
406 Some(vec![BatchPublicTransactionEvent {
407 event,
408 ..Default::default()
409 }])
410 } else {
411 let mut vec_accounts = Vec::<Vec<Pubkey>>::new();
413 let mut program_ids = Vec::new();
414
415 transaction.message.instructions.iter().for_each(|i| {
416 program_ids.push(transaction.message.account_keys[i.program_id_index as usize]);
417 vec.push(i.data.clone());
418 vec_accounts.push(
419 i.accounts
420 .iter()
421 .map(|x| transaction.message.account_keys[*x as usize])
422 .collect(),
423 );
424 });
425 simulation_result
426 .meta
427 .inner_instructions
428 .iter()
429 .flatten()
430 .find_map(|inner_instruction| {
431 vec.push(inner_instruction.instruction.data.clone());
432 program_ids.push(
433 transaction.message.account_keys
434 [inner_instruction.instruction.program_id_index as usize],
435 );
436 vec_accounts.push(
437 inner_instruction
438 .instruction
439 .accounts
440 .iter()
441 .map(|x| transaction.message.account_keys[*x as usize])
442 .collect(),
443 );
444 None::<PublicTransactionEvent>
445 });
446
447 event_from_light_transaction(
448 &program_ids.iter().map(|x| (*x).into()).collect::<Vec<_>>(),
449 vec.as_slice(),
450 vec_accounts
451 .iter()
452 .map(|inner_vec| inner_vec.iter().map(|x| (*x).into()).collect())
453 .collect(),
454 )
455 .or(Ok::<
456 Option<Vec<BatchPublicTransactionEvent>>,
457 ParseIndexerEventError,
458 >(None))?
459 };
460 if self.config.log_light_protocol_events {
461 println!("event:\n {:?}", event);
462 }
463 let event = event.map(|e| (e, signature, slot));
464
465 if let Some(indexer) = self.indexer.as_mut() {
466 if let Some(events) = event.as_ref() {
467 for event in events.0.iter() {
468 <TestIndexer as TestIndexerExtensions>::add_compressed_accounts_with_token_data(
469 indexer,
470 slot,
471 &event.event,
472 );
473 }
474 }
475 }
476
477 Ok(event)
478 }
479
480 async fn _create_and_send_transaction_with_event<T>(
481 &mut self,
482 instruction: &[Instruction],
483 payer: &Pubkey,
484 signers: &[&Keypair],
485 ) -> Result<Option<(T, Signature, Slot)>, RpcError>
486 where
487 T: BorshDeserialize + Send + Debug,
488 {
489 let transaction = Transaction::new_signed_with_payer(
490 instruction,
491 Some(payer),
492 signers,
493 self.context.latest_blockhash(),
494 );
495
496 let signature = transaction.signatures[0];
497 let simulation_result = self
501 .context
502 .simulate_transaction(transaction.clone())
503 .map_err(|x| RpcError::from(x.err))?;
504
505 let event = simulation_result
506 .meta
507 .inner_instructions
508 .iter()
509 .flatten()
510 .find_map(|inner_instruction| {
511 T::try_from_slice(&inner_instruction.instruction.data).ok()
512 });
513 self.transaction_counter += 1;
515 let _res = self.context.send_transaction(transaction).map_err(|x| {
516 if self.config.log_failed_tx {
517 println!("{}", x.meta.pretty_logs());
518 }
519 RpcError::TransactionError(x.err)
520 })?;
521 self.maybe_print_logs(_res.pretty_logs());
522
523 let slot = self.get_slot().await?;
524 let result = event.map(|event| (event, signature, slot));
525 Ok(result)
526 }
527
528 async fn _create_and_send_transaction_with_batched_event(
529 &mut self,
530 instruction: &[Instruction],
531 payer: &Pubkey,
532 signers: &[&Keypair],
533 ) -> Result<Option<(Vec<BatchPublicTransactionEvent>, Signature, Slot)>, RpcError> {
534 let transaction = Transaction::new_signed_with_payer(
535 instruction,
536 Some(payer),
537 signers,
538 self.context.latest_blockhash(),
539 );
540
541 self._send_transaction_with_batched_event(transaction).await
542 }
543}