light_test_utils/rpc/
test_rpc.rs1use std::fmt::{Debug, Formatter};
2
3use anchor_lang::prelude::Pubkey;
4use anchor_lang::solana_program::clock::Slot;
5use anchor_lang::solana_program::hash::Hash;
6use anchor_lang::solana_program::system_instruction;
7use anchor_lang::AnchorDeserialize;
8use async_trait::async_trait;
9use solana_program_test::{BanksClientError, ProgramTestContext};
10use solana_sdk::account::{Account, AccountSharedData};
11use solana_sdk::commitment_config::CommitmentConfig;
12use solana_sdk::epoch_info::EpochInfo;
13use solana_sdk::instruction::{Instruction, InstructionError};
14use solana_sdk::signature::{Keypair, Signature};
15use solana_sdk::signer::Signer;
16use solana_sdk::transaction::{Transaction, TransactionError};
17
18use light_client::rpc::errors::RpcError;
19use light_client::rpc::RpcConnection;
20use light_client::transaction_params::TransactionParams;
21
22pub struct ProgramTestRpcConnection {
23 pub context: ProgramTestContext,
24}
25
26impl Debug for ProgramTestRpcConnection {
27 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
28 write!(f, "ProgramTestRpcConnection")
29 }
30}
31
32#[async_trait]
33impl RpcConnection for ProgramTestRpcConnection {
34 fn new<U: ToString>(_url: U, _commitment_config: Option<CommitmentConfig>) -> Self
35 where
36 Self: Sized,
37 {
38 unimplemented!()
39 }
40
41 fn get_payer(&self) -> &Keypair {
42 &self.context.payer
43 }
44
45 fn get_url(&self) -> String {
46 unimplemented!("get_url doesn't make sense for ProgramTestRpcConnection")
47 }
48
49 async fn health(&self) -> Result<(), RpcError> {
50 unimplemented!()
51 }
52
53 async fn get_block_time(&self, _slot: u64) -> Result<i64, RpcError> {
54 unimplemented!()
55 }
56
57 async fn get_epoch_info(&self) -> Result<EpochInfo, RpcError> {
58 unimplemented!()
59 }
60
61 async fn get_program_accounts(
62 &self,
63 _program_id: &Pubkey,
64 ) -> Result<Vec<(Pubkey, Account)>, RpcError> {
65 unimplemented!("get_program_accounts")
66 }
67
68 async fn process_transaction(
69 &mut self,
70 transaction: Transaction,
71 ) -> Result<Signature, RpcError> {
72 let sig = *transaction.signatures.first().unwrap();
73 let result = self
74 .context
75 .banks_client
76 .process_transaction_with_metadata(transaction)
77 .await
78 .map_err(RpcError::from)?;
79 result.result.map_err(RpcError::TransactionError)?;
80 Ok(sig)
81 }
82
83 async fn process_transaction_with_context(
84 &mut self,
85 transaction: Transaction,
86 ) -> Result<(Signature, Slot), RpcError> {
87 let sig = *transaction.signatures.first().unwrap();
88 let result = self
89 .context
90 .banks_client
91 .process_transaction_with_metadata(transaction)
92 .await
93 .map_err(RpcError::from)?;
94 result.result.map_err(RpcError::TransactionError)?;
95 let slot = self.context.banks_client.get_root_slot().await?;
96 Ok((sig, slot))
97 }
98
99 async fn create_and_send_transaction_with_event<T>(
100 &mut self,
101 instruction: &[Instruction],
102 payer: &Pubkey,
103 signers: &[&Keypair],
104 transaction_params: Option<TransactionParams>,
105 ) -> Result<Option<(T, Signature, Slot)>, RpcError>
106 where
107 T: AnchorDeserialize + Send + Debug,
108 {
109 let pre_balance = self
110 .context
111 .banks_client
112 .get_account(*payer)
113 .await?
114 .unwrap()
115 .lamports;
116
117 let transaction = Transaction::new_signed_with_payer(
118 instruction,
119 Some(payer),
120 signers,
121 self.context.get_new_latest_blockhash().await?,
122 );
123
124 let signature = transaction.signatures[0];
125 let simulation_result = self
129 .context
130 .banks_client
131 .simulate_transaction(transaction.clone())
132 .await?;
133 if let Some(Err(e)) = simulation_result.result {
135 let error = match e {
136 TransactionError::InstructionError(_, _) => RpcError::TransactionError(e),
137 _ => RpcError::from(BanksClientError::TransactionError(e)),
138 };
139 return Err(error);
140 }
141
142 let event = simulation_result
144 .simulation_details
145 .and_then(|details| details.inner_instructions)
146 .and_then(|instructions| {
147 instructions.iter().flatten().find_map(|inner_instruction| {
148 T::try_from_slice(inner_instruction.instruction.data.as_slice()).ok()
149 })
150 });
151 if let Some(Ok(())) = simulation_result.result {
153 let result = self
154 .context
155 .banks_client
156 .process_transaction(transaction)
157 .await;
158 if let Err(e) = result {
159 let error = RpcError::from(e);
160 return Err(error);
161 }
162 }
163
164 if let Some(transaction_params) = transaction_params {
166 let mut deduped_signers = signers.to_vec();
167 deduped_signers.dedup();
168 let post_balance = self.get_account(*payer).await?.unwrap().lamports;
169
170 let mut network_fee: i64 = 0;
172 if transaction_params.num_input_compressed_accounts != 0 {
173 network_fee += transaction_params.fee_config.network_fee as i64;
174 }
175 if transaction_params.num_new_addresses != 0 {
176 network_fee += transaction_params.fee_config.address_network_fee as i64;
177 }
178 let expected_post_balance = pre_balance as i64
179 - i64::from(transaction_params.num_new_addresses)
180 * transaction_params.fee_config.address_queue_rollover as i64
181 - i64::from(transaction_params.num_output_compressed_accounts)
182 * transaction_params.fee_config.state_merkle_tree_rollover as i64
183 - transaction_params.compress
184 - transaction_params.fee_config.solana_network_fee * deduped_signers.len() as i64
185 - network_fee;
186
187 if post_balance as i64 != expected_post_balance {
188 println!("transaction_params: {:?}", transaction_params);
189 println!("pre_balance: {}", pre_balance);
190 println!("post_balance: {}", post_balance);
191 println!("expected post_balance: {}", expected_post_balance);
192 println!(
193 "diff post_balance: {}",
194 post_balance as i64 - expected_post_balance
195 );
196 println!(
197 "rollover fee: {}",
198 transaction_params.fee_config.state_merkle_tree_rollover
199 );
200 println!(
201 "address_network_fee: {}",
202 transaction_params.fee_config.address_network_fee
203 );
204 println!("network_fee: {}", network_fee);
205 println!("num signers {}", deduped_signers.len());
206 return Err(RpcError::from(BanksClientError::TransactionError(
207 TransactionError::InstructionError(0, InstructionError::Custom(11111)),
208 )));
209 }
210 }
211
212 let slot = self.context.banks_client.get_root_slot().await?;
213 let result = event.map(|event| (event, signature, slot));
214 Ok(result)
215 }
216
217 async fn confirm_transaction(&self, _transaction: Signature) -> Result<bool, RpcError> {
218 Ok(true)
219 }
220
221 async fn get_account(&mut self, address: Pubkey) -> Result<Option<Account>, RpcError> {
222 self.context
223 .banks_client
224 .get_account(address)
225 .await
226 .map_err(RpcError::from)
227 }
228
229 fn set_account(&mut self, address: &Pubkey, account: &AccountSharedData) {
230 self.context.set_account(address, account);
231 }
232
233 async fn get_minimum_balance_for_rent_exemption(
234 &mut self,
235 data_len: usize,
236 ) -> Result<u64, RpcError> {
237 let rent = self
238 .context
239 .banks_client
240 .get_rent()
241 .await
242 .map_err(RpcError::from);
243
244 Ok(rent?.minimum_balance(data_len))
245 }
246
247 async fn airdrop_lamports(
248 &mut self,
249 to: &Pubkey,
250 lamports: u64,
251 ) -> Result<Signature, RpcError> {
252 let transfer_instruction =
254 system_instruction::transfer(&self.context.payer.pubkey(), to, lamports);
255 let latest_blockhash = self.get_latest_blockhash().await.unwrap();
256 let transaction = Transaction::new_signed_with_payer(
258 &[transfer_instruction],
259 Some(&self.get_payer().pubkey()),
260 &vec![&self.get_payer()],
261 latest_blockhash,
262 );
263 let sig = *transaction.signatures.first().unwrap();
264
265 self.context
267 .banks_client
268 .process_transaction(transaction)
269 .await?;
270
271 Ok(sig)
272 }
273
274 async fn get_balance(&mut self, pubkey: &Pubkey) -> Result<u64, RpcError> {
275 self.context
276 .banks_client
277 .get_balance(*pubkey)
278 .await
279 .map_err(RpcError::from)
280 }
281
282 async fn get_latest_blockhash(&mut self) -> Result<Hash, RpcError> {
283 self.context
284 .get_new_latest_blockhash()
285 .await
286 .map_err(|e| RpcError::from(BanksClientError::from(e)))
287 }
288
289 async fn get_slot(&mut self) -> Result<u64, RpcError> {
290 self.context
291 .banks_client
292 .get_root_slot()
293 .await
294 .map_err(RpcError::from)
295 }
296
297 async fn warp_to_slot(&mut self, slot: Slot) -> Result<(), RpcError> {
298 self.context
299 .warp_to_slot(slot)
300 .map_err(|_| RpcError::InvalidWarpSlot)
301 }
302
303 async fn send_transaction(&self, _transaction: &Transaction) -> Result<Signature, RpcError> {
304 unimplemented!("send transaction is unimplemented for ProgramTestRpcConnection")
305 }
306}