Skip to main content

solana_runtime/
bank_client.rs

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    /// Create and process a transaction from a single instruction.
71    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    /// Transfer `lamports` from `keypair` to `pubkey`
81    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        // https://github.com/solana-labs/solana/issues/7199
167        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        // Create 2-2 Multisig Transfer instruction.
323        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}