gemachain_runtime/
bank_client.rs

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