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