Skip to main content

solana_runtime/
bank_client.rs

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