use crossbeam_channel::{Receiver, RecvTimeoutError}; use solana_ledger::{blockstore::Blockstore, blockstore_processor::TransactionStatusBatch}; use solana_runtime::{ bank::{Bank, HashAgeKind}, nonce_utils, }; use solana_transaction_status::TransactionStatusMeta; use std::{ sync::{ atomic::{AtomicBool, Ordering}, Arc, }, thread::{self, Builder, JoinHandle}, time::Duration, }; pub struct TransactionStatusService { thread_hdl: JoinHandle<()>, } impl TransactionStatusService { #[allow(clippy::new_ret_no_self)] pub fn new( write_transaction_status_receiver: Receiver, blockstore: Arc, exit: &Arc, ) -> Self { let exit = exit.clone(); let thread_hdl = Builder::new() .name("solana-transaction-status-writer".to_string()) .spawn(move || loop { if exit.load(Ordering::Relaxed) { break; } if let Err(RecvTimeoutError::Disconnected) = Self::write_transaction_status_batch( &write_transaction_status_receiver, &blockstore, ) { break; } }) .unwrap(); Self { thread_hdl } } fn write_transaction_status_batch( write_transaction_status_receiver: &Receiver, blockstore: &Arc, ) -> Result<(), RecvTimeoutError> { let TransactionStatusBatch { bank, transactions, statuses, balances, } = write_transaction_status_receiver.recv_timeout(Duration::from_secs(1))?; let slot = bank.slot(); for (((transaction, (status, hash_age_kind)), pre_balances), post_balances) in transactions .iter() .zip(statuses) .zip(balances.pre_balances) .zip(balances.post_balances) { if Bank::can_commit(&status) && !transaction.signatures.is_empty() { let fee_calculator = match hash_age_kind { Some(HashAgeKind::DurableNonce(_, account)) => { nonce_utils::fee_calculator_of(&account) } _ => bank.get_fee_calculator(&transaction.message().recent_blockhash), } .expect("FeeCalculator must exist"); let fee = fee_calculator.calculate_fee(transaction.message()); let (writable_keys, readonly_keys) = transaction.message.get_account_keys_by_lock_type(); blockstore .write_transaction_status( slot, transaction.signatures[0], writable_keys, readonly_keys, &TransactionStatusMeta { status, fee, pre_balances, post_balances, }, ) .expect("Expect database write to succeed"); } } Ok(()) } pub fn join(self) -> thread::Result<()> { self.thread_hdl.join() } }