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::SysvarSerialize,
17 solana_transaction::{versioned::VersionedTransaction, Transaction},
18 solana_transaction_error::{TransportError, TransportResult as Result},
19 std::{
20 io,
21 sync::Arc,
22 thread::{sleep, Builder},
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 {crate::bank_forks::BankForks, solana_clock as clock, std::sync::RwLock};
31
32pub struct BankClient {
33 bank: Arc<Bank>,
34 transaction_sender: Sender<VersionedTransaction>,
35}
36
37impl Client for BankClient {
38 fn tpu_addr(&self) -> String {
39 "Local BankClient".to_string()
40 }
41}
42
43impl AsyncClient for BankClient {
44 fn async_send_versioned_transaction(
45 &self,
46 transaction: VersionedTransaction,
47 ) -> Result<Signature> {
48 let signature = transaction.signatures.first().cloned().unwrap_or_default();
49 let transaction_sender = self.transaction_sender.clone();
50 transaction_sender.send(transaction).unwrap();
51 Ok(signature)
52 }
53}
54
55impl SyncClient for BankClient {
56 fn send_and_confirm_message<T: Signers + ?Sized>(
57 &self,
58 keypairs: &T,
59 message: Message,
60 ) -> Result<Signature> {
61 let blockhash = self.bank.last_blockhash();
62 let transaction = Transaction::new(keypairs, message, blockhash);
63 self.bank.process_transaction(&transaction)?;
64 Ok(transaction.signatures.first().cloned().unwrap_or_default())
65 }
66
67 fn send_and_confirm_instruction(
69 &self,
70 keypair: &Keypair,
71 instruction: Instruction,
72 ) -> Result<Signature> {
73 let message = Message::new(&[instruction], Some(&keypair.pubkey()));
74 self.send_and_confirm_message(&[keypair], message)
75 }
76
77 fn transfer_and_confirm(
79 &self,
80 lamports: u64,
81 keypair: &Keypair,
82 pubkey: &Pubkey,
83 ) -> Result<Signature> {
84 let transfer_instruction =
85 system_instruction::transfer(&keypair.pubkey(), pubkey, lamports);
86 self.send_and_confirm_instruction(keypair, transfer_instruction)
87 }
88
89 fn get_account_data(&self, pubkey: &Pubkey) -> Result<Option<Vec<u8>>> {
90 Ok(self
91 .bank
92 .get_account(pubkey)
93 .map(|account| Account::from(account).data))
94 }
95
96 fn get_account(&self, pubkey: &Pubkey) -> Result<Option<Account>> {
97 Ok(self.bank.get_account(pubkey).map(Account::from))
98 }
99
100 fn get_account_with_commitment(
101 &self,
102 pubkey: &Pubkey,
103 _commitment_config: CommitmentConfig,
104 ) -> Result<Option<Account>> {
105 Ok(self.bank.get_account(pubkey).map(Account::from))
106 }
107
108 fn get_balance(&self, pubkey: &Pubkey) -> Result<u64> {
109 Ok(self.bank.get_balance(pubkey))
110 }
111
112 fn get_balance_with_commitment(
113 &self,
114 pubkey: &Pubkey,
115 _commitment_config: CommitmentConfig,
116 ) -> Result<u64> {
117 Ok(self.bank.get_balance(pubkey))
118 }
119
120 fn get_minimum_balance_for_rent_exemption(&self, data_len: usize) -> Result<u64> {
121 Ok(self.bank.get_minimum_balance_for_rent_exemption(data_len))
122 }
123
124 fn get_signature_status(
125 &self,
126 signature: &Signature,
127 ) -> Result<Option<transaction::Result<()>>> {
128 Ok(self.bank.get_signature_status(signature))
129 }
130
131 fn get_signature_status_with_commitment(
132 &self,
133 signature: &Signature,
134 _commitment_config: CommitmentConfig,
135 ) -> Result<Option<transaction::Result<()>>> {
136 Ok(self.bank.get_signature_status(signature))
137 }
138
139 fn get_slot(&self) -> Result<u64> {
140 Ok(self.bank.slot())
141 }
142
143 fn get_slot_with_commitment(&self, _commitment_config: CommitmentConfig) -> Result<u64> {
144 Ok(self.bank.slot())
145 }
146
147 fn get_transaction_count(&self) -> Result<u64> {
148 Ok(self.bank.transaction_count())
149 }
150
151 fn get_transaction_count_with_commitment(
152 &self,
153 _commitment_config: CommitmentConfig,
154 ) -> Result<u64> {
155 Ok(self.bank.transaction_count())
156 }
157
158 fn poll_for_signature_confirmation(
159 &self,
160 signature: &Signature,
161 min_confirmed_blocks: usize,
162 ) -> Result<usize> {
163 assert_eq!(
165 min_confirmed_blocks, 1,
166 "BankClient cannot observe the passage of multiple blocks, so min_confirmed_blocks \
167 must be 1"
168 );
169 let now = Instant::now();
170 let confirmed_blocks;
171 loop {
172 if self.bank.get_signature_status(signature).is_some() {
173 confirmed_blocks = 1;
174 break;
175 }
176 if now.elapsed().as_secs() > 15 {
177 return Err(TransportError::IoError(io::Error::other(format!(
178 "signature not found after {} seconds",
179 now.elapsed().as_secs()
180 ))));
181 }
182 sleep(Duration::from_millis(250));
183 }
184 Ok(confirmed_blocks)
185 }
186
187 fn poll_for_signature(&self, signature: &Signature) -> Result<()> {
188 let now = Instant::now();
189 loop {
190 let response = self.bank.get_signature_status(signature);
191 if let Some(res) = response {
192 if res.is_ok() {
193 break;
194 }
195 }
196 if now.elapsed().as_secs() > 15 {
197 return Err(TransportError::IoError(io::Error::other(format!(
198 "signature not found after {} seconds",
199 now.elapsed().as_secs()
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(|| TransportError::IoError(io::Error::other("Unable calculate fee")))
243 }
244}
245
246impl BankClient {
247 fn run(bank: &Bank, transaction_receiver: Receiver<VersionedTransaction>) {
248 while let Ok(tx) = transaction_receiver.recv() {
249 let mut transactions = vec![tx];
250 while let Ok(tx) = transaction_receiver.try_recv() {
251 transactions.push(tx);
252 }
253 let _ = bank.try_process_entry_transactions(transactions);
254 }
255 }
256
257 pub fn new_shared(bank: Arc<Bank>) -> Self {
258 let (transaction_sender, transaction_receiver) = unbounded();
259 let thread_bank = bank.clone();
260 Builder::new()
261 .name("solBankClient".to_string())
262 .spawn(move || Self::run(&thread_bank, transaction_receiver))
263 .unwrap();
264 Self {
265 bank,
266 transaction_sender,
267 }
268 }
269
270 pub fn new(bank: Bank) -> Self {
271 Self::new_shared(Arc::new(bank))
272 }
273
274 pub fn set_sysvar_for_tests<T: SysvarSerialize>(&self, sysvar: &T) {
275 self.bank.set_sysvar_for_tests(sysvar);
276 }
277
278 #[cfg(feature = "dev-context-only-utils")]
279 pub fn advance_slot(
280 &mut self,
281 by: u64,
282 bank_forks: &RwLock<BankForks>,
283 collector_id: &Pubkey,
284 ) -> Option<Arc<Bank>> {
285 let new_bank = Bank::new_from_parent(
286 self.bank.clone(),
287 collector_id,
288 self.bank.slot().checked_add(by)?,
289 );
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}