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