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