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        hash::Hash,
10        instruction::Instruction,
11        message::{Message, SanitizedMessage},
12        pubkey::Pubkey,
13        signature::{Keypair, Signature, Signer},
14        signers::Signers,
15        system_instruction,
16        sysvar::{Sysvar, SysvarId},
17        transaction::{self, Transaction, VersionedTransaction},
18        transport::{Result, TransportError},
19    },
20    std::{
21        io,
22        sync::{Arc, Mutex},
23        thread::{sleep, Builder},
24        time::{Duration, Instant},
25    },
26};
27#[cfg(feature = "dev-context-only-utils")]
28use {crate::bank_forks::BankForks, solana_sdk::clock, std::sync::RwLock};
29
30pub struct BankClient {
31    bank: Arc<Bank>,
32    transaction_sender: Mutex<Sender<VersionedTransaction>>,
33}
34
35impl Client for BankClient {
36    fn tpu_addr(&self) -> String {
37        "Local BankClient".to_string()
38    }
39}
40
41impl AsyncClient for BankClient {
42    fn async_send_versioned_transaction(
43        &self,
44        transaction: VersionedTransaction,
45    ) -> Result<Signature> {
46        let signature = transaction.signatures.first().cloned().unwrap_or_default();
47        let transaction_sender = self.transaction_sender.lock().unwrap();
48        transaction_sender.send(transaction).unwrap();
49        Ok(signature)
50    }
51}
52
53impl SyncClient for BankClient {
54    fn send_and_confirm_message<T: Signers + ?Sized>(
55        &self,
56        keypairs: &T,
57        message: Message,
58    ) -> Result<Signature> {
59        let blockhash = self.bank.last_blockhash();
60        let transaction = Transaction::new(keypairs, message, blockhash);
61        self.bank.process_transaction(&transaction)?;
62        Ok(transaction.signatures.first().cloned().unwrap_or_default())
63    }
64
65    /// Create and process a transaction from a single instruction.
66    fn send_and_confirm_instruction(
67        &self,
68        keypair: &Keypair,
69        instruction: Instruction,
70    ) -> Result<Signature> {
71        let message = Message::new(&[instruction], Some(&keypair.pubkey()));
72        self.send_and_confirm_message(&[keypair], message)
73    }
74
75    /// Transfer `lamports` from `keypair` to `pubkey`
76    fn transfer_and_confirm(
77        &self,
78        lamports: u64,
79        keypair: &Keypair,
80        pubkey: &Pubkey,
81    ) -> Result<Signature> {
82        let transfer_instruction =
83            system_instruction::transfer(&keypair.pubkey(), pubkey, lamports);
84        self.send_and_confirm_instruction(keypair, transfer_instruction)
85    }
86
87    fn get_account_data(&self, pubkey: &Pubkey) -> Result<Option<Vec<u8>>> {
88        Ok(self
89            .bank
90            .get_account(pubkey)
91            .map(|account| Account::from(account).data))
92    }
93
94    fn get_account(&self, pubkey: &Pubkey) -> Result<Option<Account>> {
95        Ok(self.bank.get_account(pubkey).map(Account::from))
96    }
97
98    fn get_account_with_commitment(
99        &self,
100        pubkey: &Pubkey,
101        _commitment_config: CommitmentConfig,
102    ) -> Result<Option<Account>> {
103        Ok(self.bank.get_account(pubkey).map(Account::from))
104    }
105
106    fn get_balance(&self, pubkey: &Pubkey) -> Result<u64> {
107        Ok(self.bank.get_balance(pubkey))
108    }
109
110    fn get_balance_with_commitment(
111        &self,
112        pubkey: &Pubkey,
113        _commitment_config: CommitmentConfig,
114    ) -> Result<u64> {
115        Ok(self.bank.get_balance(pubkey))
116    }
117
118    fn get_minimum_balance_for_rent_exemption(&self, data_len: usize) -> Result<u64> {
119        Ok(self.bank.get_minimum_balance_for_rent_exemption(data_len))
120    }
121
122    fn get_signature_status(
123        &self,
124        signature: &Signature,
125    ) -> Result<Option<transaction::Result<()>>> {
126        Ok(self.bank.get_signature_status(signature))
127    }
128
129    fn get_signature_status_with_commitment(
130        &self,
131        signature: &Signature,
132        _commitment_config: CommitmentConfig,
133    ) -> Result<Option<transaction::Result<()>>> {
134        Ok(self.bank.get_signature_status(signature))
135    }
136
137    fn get_slot(&self) -> Result<u64> {
138        Ok(self.bank.slot())
139    }
140
141    fn get_slot_with_commitment(&self, _commitment_config: CommitmentConfig) -> Result<u64> {
142        Ok(self.bank.slot())
143    }
144
145    fn get_transaction_count(&self) -> Result<u64> {
146        Ok(self.bank.transaction_count())
147    }
148
149    fn get_transaction_count_with_commitment(
150        &self,
151        _commitment_config: CommitmentConfig,
152    ) -> Result<u64> {
153        Ok(self.bank.transaction_count())
154    }
155
156    fn poll_for_signature_confirmation(
157        &self,
158        signature: &Signature,
159        min_confirmed_blocks: usize,
160    ) -> Result<usize> {
161        // https://github.com/solana-labs/solana/issues/7199
162        assert_eq!(min_confirmed_blocks, 1, "BankClient cannot observe the passage of multiple blocks, so min_confirmed_blocks must be 1");
163        let now = Instant::now();
164        let confirmed_blocks;
165        loop {
166            if self.bank.get_signature_status(signature).is_some() {
167                confirmed_blocks = 1;
168                break;
169            }
170            if now.elapsed().as_secs() > 15 {
171                return Err(TransportError::IoError(io::Error::new(
172                    io::ErrorKind::Other,
173                    format!(
174                        "signature not found after {} seconds",
175                        now.elapsed().as_secs()
176                    ),
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::new(
195                    io::ErrorKind::Other,
196                    format!(
197                        "signature not found after {} seconds",
198                        now.elapsed().as_secs()
199                    ),
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(|| {
243            TransportError::IoError(io::Error::new(io::ErrorKind::Other, "Unable calculate fee"))
244        })
245    }
246}
247
248impl BankClient {
249    fn run(bank: &Bank, transaction_receiver: Receiver<VersionedTransaction>) {
250        while let Ok(tx) = transaction_receiver.recv() {
251            let mut transactions = vec![tx];
252            while let Ok(tx) = transaction_receiver.try_recv() {
253                transactions.push(tx);
254            }
255            let _ = bank.try_process_entry_transactions(transactions);
256        }
257    }
258
259    pub fn new_shared(bank: Arc<Bank>) -> Self {
260        let (transaction_sender, transaction_receiver) = unbounded();
261        let transaction_sender = Mutex::new(transaction_sender);
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: Sysvar + SysvarId>(&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        collector_id: &Pubkey,
287    ) -> Option<Arc<Bank>> {
288        let new_bank = Bank::new_from_parent(
289            self.bank.clone(),
290            collector_id,
291            self.bank.slot().checked_add(by)?,
292        );
293        self.bank = bank_forks
294            .write()
295            .unwrap()
296            .insert(new_bank)
297            .clone_without_scheduler();
298
299        self.set_sysvar_for_tests(&clock::Clock {
300            slot: self.bank.slot(),
301            ..clock::Clock::default()
302        });
303        Some(self.bank.clone())
304    }
305}
306
307#[cfg(test)]
308mod tests {
309    use {
310        super::*,
311        solana_sdk::{
312            genesis_config::create_genesis_config, instruction::AccountMeta,
313            native_token::sol_to_lamports,
314        },
315    };
316
317    #[test]
318    fn test_bank_client_new_with_keypairs() {
319        let (genesis_config, john_doe_keypair) = create_genesis_config(sol_to_lamports(1.0));
320        let john_pubkey = john_doe_keypair.pubkey();
321        let jane_doe_keypair = Keypair::new();
322        let jane_pubkey = jane_doe_keypair.pubkey();
323        let doe_keypairs = vec![&john_doe_keypair, &jane_doe_keypair];
324        let (bank, _bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
325        let bank_client = BankClient::new_shared(bank);
326        let amount = genesis_config.rent.minimum_balance(0);
327
328        // Create 2-2 Multisig Transfer instruction.
329        let bob_pubkey = solana_pubkey::new_rand();
330        let mut transfer_instruction =
331            system_instruction::transfer(&john_pubkey, &bob_pubkey, amount);
332        transfer_instruction
333            .accounts
334            .push(AccountMeta::new(jane_pubkey, true));
335
336        let message = Message::new(&[transfer_instruction], Some(&john_pubkey));
337        bank_client
338            .send_and_confirm_message(&doe_keypairs, message)
339            .unwrap();
340        assert_eq!(bank_client.get_balance(&bob_pubkey).unwrap(), amount);
341    }
342}