1use {
2 crate::bank::Bank,
3 crossbeam_channel::{unbounded, Receiver, Sender},
4 solana_sdk::{
5 account::Account,
6 client::{AsyncClient, Client, SyncClient},
7 commitment_config::CommitmentConfig,
8 epoch_info::EpochInfo,
9 fee_calculator::{FeeCalculator, FeeRateGovernor},
10 hash::Hash,
11 instruction::Instruction,
12 message::{Message, SanitizedMessage},
13 pubkey::Pubkey,
14 signature::{Keypair, Signature, Signer},
15 signers::Signers,
16 system_instruction,
17 transaction::{self, Transaction, VersionedTransaction},
18 transport::{Result, TransportError},
19 },
20 std::{
21 convert::TryFrom,
22 io,
23 sync::{Arc, Mutex},
24 thread::{sleep, Builder},
25 time::{Duration, Instant},
26 },
27};
28
29pub struct BankClient {
30 bank: Arc<Bank>,
31 transaction_sender: Mutex<Sender<VersionedTransaction>>,
32}
33
34impl Client for BankClient {
35 fn tpu_addr(&self) -> String {
36 "Local BankClient".to_string()
37 }
38}
39
40impl AsyncClient for BankClient {
41 fn async_send_versioned_transaction(
42 &self,
43 transaction: VersionedTransaction,
44 ) -> Result<Signature> {
45 let signature = transaction.signatures.get(0).cloned().unwrap_or_default();
46 let transaction_sender = self.transaction_sender.lock().unwrap();
47 transaction_sender.send(transaction).unwrap();
48 Ok(signature)
49 }
50}
51
52impl SyncClient for BankClient {
53 fn send_and_confirm_message<T: Signers>(
54 &self,
55 keypairs: &T,
56 message: Message,
57 ) -> Result<Signature> {
58 let blockhash = self.bank.last_blockhash();
59 let transaction = Transaction::new(keypairs, message, blockhash);
60 self.bank.process_transaction(&transaction)?;
61 Ok(transaction.signatures.get(0).cloned().unwrap_or_default())
62 }
63
64 fn send_and_confirm_instruction(
66 &self,
67 keypair: &Keypair,
68 instruction: Instruction,
69 ) -> Result<Signature> {
70 let message = Message::new(&[instruction], Some(&keypair.pubkey()));
71 self.send_and_confirm_message(&[keypair], message)
72 }
73
74 fn transfer_and_confirm(
76 &self,
77 lamports: u64,
78 keypair: &Keypair,
79 pubkey: &Pubkey,
80 ) -> Result<Signature> {
81 let transfer_instruction =
82 system_instruction::transfer(&keypair.pubkey(), pubkey, lamports);
83 self.send_and_confirm_instruction(keypair, transfer_instruction)
84 }
85
86 fn get_account_data(&self, pubkey: &Pubkey) -> Result<Option<Vec<u8>>> {
87 Ok(self
88 .bank
89 .get_account(pubkey)
90 .map(|account| Account::from(account).data))
91 }
92
93 fn get_account(&self, pubkey: &Pubkey) -> Result<Option<Account>> {
94 Ok(self.bank.get_account(pubkey).map(Account::from))
95 }
96
97 fn get_account_with_commitment(
98 &self,
99 pubkey: &Pubkey,
100 _commitment_config: CommitmentConfig,
101 ) -> Result<Option<Account>> {
102 Ok(self.bank.get_account(pubkey).map(Account::from))
103 }
104
105 fn get_balance(&self, pubkey: &Pubkey) -> Result<u64> {
106 Ok(self.bank.get_balance(pubkey))
107 }
108
109 fn get_balance_with_commitment(
110 &self,
111 pubkey: &Pubkey,
112 _commitment_config: CommitmentConfig,
113 ) -> Result<u64> {
114 Ok(self.bank.get_balance(pubkey))
115 }
116
117 fn get_minimum_balance_for_rent_exemption(&self, data_len: usize) -> Result<u64> {
118 Ok(self.bank.get_minimum_balance_for_rent_exemption(data_len))
119 }
120
121 fn get_recent_blockhash(&self) -> Result<(Hash, FeeCalculator)> {
122 Ok((
123 self.bank.last_blockhash(),
124 FeeCalculator::new(self.bank.get_lamports_per_signature()),
125 ))
126 }
127
128 fn get_recent_blockhash_with_commitment(
129 &self,
130 _commitment_config: CommitmentConfig,
131 ) -> Result<(Hash, FeeCalculator, u64)> {
132 let blockhash = self.bank.last_blockhash();
133 #[allow(deprecated)]
134 let last_valid_slot = self
135 .bank
136 .get_blockhash_last_valid_slot(&blockhash)
137 .expect("bank blockhash queue should contain blockhash");
138 Ok((
139 blockhash,
140 FeeCalculator::new(self.bank.get_lamports_per_signature()),
141 last_valid_slot,
142 ))
143 }
144
145 fn get_fee_calculator_for_blockhash(&self, blockhash: &Hash) -> Result<Option<FeeCalculator>> {
146 Ok(self
147 .bank
148 .get_lamports_per_signature_for_blockhash(blockhash)
149 .map(FeeCalculator::new))
150 }
151
152 fn get_fee_rate_governor(&self) -> Result<FeeRateGovernor> {
153 #[allow(deprecated)]
154 Ok(self.bank.get_fee_rate_governor().clone())
155 }
156
157 fn get_signature_status(
158 &self,
159 signature: &Signature,
160 ) -> Result<Option<transaction::Result<()>>> {
161 Ok(self.bank.get_signature_status(signature))
162 }
163
164 fn get_signature_status_with_commitment(
165 &self,
166 signature: &Signature,
167 _commitment_config: CommitmentConfig,
168 ) -> Result<Option<transaction::Result<()>>> {
169 Ok(self.bank.get_signature_status(signature))
170 }
171
172 fn get_slot(&self) -> Result<u64> {
173 Ok(self.bank.slot())
174 }
175
176 fn get_slot_with_commitment(&self, _commitment_config: CommitmentConfig) -> Result<u64> {
177 Ok(self.bank.slot())
178 }
179
180 fn get_transaction_count(&self) -> Result<u64> {
181 Ok(self.bank.transaction_count())
182 }
183
184 fn get_transaction_count_with_commitment(
185 &self,
186 _commitment_config: CommitmentConfig,
187 ) -> Result<u64> {
188 Ok(self.bank.transaction_count())
189 }
190
191 fn poll_for_signature_confirmation(
192 &self,
193 signature: &Signature,
194 min_confirmed_blocks: usize,
195 ) -> Result<usize> {
196 assert_eq!(min_confirmed_blocks, 1, "BankClient cannot observe the passage of multiple blocks, so min_confirmed_blocks must be 1");
198 let now = Instant::now();
199 let confirmed_blocks;
200 loop {
201 if self.bank.get_signature_status(signature).is_some() {
202 confirmed_blocks = 1;
203 break;
204 }
205 if now.elapsed().as_secs() > 15 {
206 return Err(TransportError::IoError(io::Error::new(
207 io::ErrorKind::Other,
208 format!(
209 "signature not found after {} seconds",
210 now.elapsed().as_secs()
211 ),
212 )));
213 }
214 sleep(Duration::from_millis(250));
215 }
216 Ok(confirmed_blocks)
217 }
218
219 fn poll_for_signature(&self, signature: &Signature) -> Result<()> {
220 let now = Instant::now();
221 loop {
222 let response = self.bank.get_signature_status(signature);
223 if let Some(res) = response {
224 if res.is_ok() {
225 break;
226 }
227 }
228 if now.elapsed().as_secs() > 15 {
229 return Err(TransportError::IoError(io::Error::new(
230 io::ErrorKind::Other,
231 format!(
232 "signature not found after {} seconds",
233 now.elapsed().as_secs()
234 ),
235 )));
236 }
237 sleep(Duration::from_millis(250));
238 }
239 Ok(())
240 }
241
242 fn get_new_blockhash(&self, blockhash: &Hash) -> Result<(Hash, FeeCalculator)> {
243 let recent_blockhash = self.get_latest_blockhash()?;
244 if recent_blockhash != *blockhash {
245 Ok((
246 recent_blockhash,
247 FeeCalculator::new(self.bank.get_lamports_per_signature()),
248 ))
249 } else {
250 Err(TransportError::IoError(io::Error::new(
251 io::ErrorKind::Other,
252 "Unable to get new blockhash",
253 )))
254 }
255 }
256
257 fn get_epoch_info(&self) -> Result<EpochInfo> {
258 Ok(self.bank.get_epoch_info())
259 }
260
261 fn get_latest_blockhash(&self) -> Result<Hash> {
262 Ok(self.bank.last_blockhash())
263 }
264
265 fn get_latest_blockhash_with_commitment(
266 &self,
267 _commitment_config: CommitmentConfig,
268 ) -> Result<(Hash, u64)> {
269 let blockhash = self.bank.last_blockhash();
270 let last_valid_block_height = self
271 .bank
272 .get_blockhash_last_valid_block_height(&blockhash)
273 .expect("bank blockhash queue should contain blockhash");
274 Ok((blockhash, last_valid_block_height))
275 }
276
277 fn is_blockhash_valid(
278 &self,
279 blockhash: &Hash,
280 _commitment_config: CommitmentConfig,
281 ) -> Result<bool> {
282 Ok(self.bank.is_blockhash_valid(blockhash))
283 }
284
285 fn get_fee_for_message(&self, message: &Message) -> Result<u64> {
286 SanitizedMessage::try_from(message.clone())
287 .ok()
288 .and_then(|sanitized_message| self.bank.get_fee_for_message(&sanitized_message))
289 .ok_or_else(|| {
290 TransportError::IoError(io::Error::new(
291 io::ErrorKind::Other,
292 "Unable calculate fee",
293 ))
294 })
295 }
296}
297
298impl BankClient {
299 fn run(bank: &Bank, transaction_receiver: Receiver<VersionedTransaction>) {
300 while let Ok(tx) = transaction_receiver.recv() {
301 let mut transactions = vec![tx];
302 while let Ok(tx) = transaction_receiver.try_recv() {
303 transactions.push(tx);
304 }
305 let _ = bank.try_process_entry_transactions(transactions);
306 }
307 }
308
309 pub fn new_shared(bank: &Arc<Bank>) -> Self {
310 let (transaction_sender, transaction_receiver) = unbounded();
311 let transaction_sender = Mutex::new(transaction_sender);
312 let thread_bank = bank.clone();
313 let bank = bank.clone();
314 Builder::new()
315 .name("solBankClient".to_string())
316 .spawn(move || Self::run(&thread_bank, transaction_receiver))
317 .unwrap();
318 Self {
319 bank,
320 transaction_sender,
321 }
322 }
323
324 pub fn new(bank: Bank) -> Self {
325 Self::new_shared(&Arc::new(bank))
326 }
327}
328
329#[cfg(test)]
330mod tests {
331 use {
332 super::*,
333 solana_sdk::{
334 genesis_config::create_genesis_config, instruction::AccountMeta,
335 native_token::sol_to_lamports,
336 },
337 };
338
339 #[test]
340 fn test_bank_client_new_with_keypairs() {
341 let (genesis_config, john_doe_keypair) = create_genesis_config(sol_to_lamports(1.0));
342 let john_pubkey = john_doe_keypair.pubkey();
343 let jane_doe_keypair = Keypair::new();
344 let jane_pubkey = jane_doe_keypair.pubkey();
345 let doe_keypairs = vec![&john_doe_keypair, &jane_doe_keypair];
346 let bank = Bank::new_for_tests(&genesis_config);
347 let bank_client = BankClient::new(bank);
348 let amount = genesis_config.rent.minimum_balance(0);
349
350 let bob_pubkey = solana_sdk::pubkey::new_rand();
352 let mut transfer_instruction =
353 system_instruction::transfer(&john_pubkey, &bob_pubkey, amount);
354 transfer_instruction
355 .accounts
356 .push(AccountMeta::new(jane_pubkey, true));
357
358 let message = Message::new(&[transfer_instruction], Some(&john_pubkey));
359 bank_client
360 .send_and_confirm_message(&doe_keypairs, message)
361 .unwrap();
362 assert_eq!(bank_client.get_balance(&bob_pubkey).unwrap(), amount);
363 }
364}