solana_runtime/
bank_client.rs

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