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