2023-06-12 12:30:47 -07:00
|
|
|
// This class will manage the lifecycle for a transaction
|
|
|
|
// It will send, replay if necessary and confirm by listening to blocks
|
|
|
|
|
2023-07-20 04:45:54 -07:00
|
|
|
use std::time::Duration;
|
2023-06-12 12:30:47 -07:00
|
|
|
|
|
|
|
use crate::{
|
|
|
|
tpu_utils::tpu_service::TpuService,
|
2023-09-09 08:43:51 -07:00
|
|
|
transaction_replayer::{TransactionReplay, TransactionReplayer, MESSAGES_IN_REPLAY_QUEUE},
|
2023-09-20 07:57:01 -07:00
|
|
|
tx_sender::TxSender,
|
2023-06-12 12:30:47 -07:00
|
|
|
};
|
|
|
|
use anyhow::bail;
|
2023-09-20 07:57:01 -07:00
|
|
|
use solana_lite_rpc_core::{solana_utils::SerializableTransaction, types::SlotStream, structures::transaction_sent_info::SentTransactionInfo};
|
2023-06-12 12:30:47 -07:00
|
|
|
use solana_lite_rpc_core::{
|
2023-09-13 08:15:28 -07:00
|
|
|
stores::block_information_store::{BlockInformation, BlockInformationStore},
|
|
|
|
structures::notifications::NotificationSender,
|
2023-07-20 04:45:54 -07:00
|
|
|
AnyhowJoinHandle,
|
2023-06-12 12:30:47 -07:00
|
|
|
};
|
2023-08-31 03:34:13 -07:00
|
|
|
use solana_sdk::transaction::VersionedTransaction;
|
2023-06-12 12:30:47 -07:00
|
|
|
use tokio::{
|
|
|
|
sync::mpsc::{self, Sender, UnboundedSender},
|
|
|
|
time::Instant,
|
|
|
|
};
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct TransactionServiceBuilder {
|
|
|
|
tx_sender: TxSender,
|
|
|
|
tx_replayer: TransactionReplayer,
|
|
|
|
tpu_service: TpuService,
|
|
|
|
max_nb_txs_in_queue: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TransactionServiceBuilder {
|
|
|
|
pub fn new(
|
|
|
|
tx_sender: TxSender,
|
|
|
|
tx_replayer: TransactionReplayer,
|
|
|
|
tpu_service: TpuService,
|
|
|
|
max_nb_txs_in_queue: usize,
|
|
|
|
) -> Self {
|
|
|
|
Self {
|
|
|
|
tx_sender,
|
|
|
|
tx_replayer,
|
|
|
|
tpu_service,
|
|
|
|
max_nb_txs_in_queue,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-20 04:45:54 -07:00
|
|
|
pub fn start(
|
|
|
|
self,
|
2023-06-12 12:30:47 -07:00
|
|
|
notifier: Option<NotificationSender>,
|
2023-08-31 03:34:13 -07:00
|
|
|
block_store: BlockInformationStore,
|
2023-06-12 12:30:47 -07:00
|
|
|
max_retries: usize,
|
2023-08-31 03:34:13 -07:00
|
|
|
slot_notifications: SlotStream,
|
2023-07-20 04:45:54 -07:00
|
|
|
) -> (TransactionService, AnyhowJoinHandle) {
|
2023-06-12 12:30:47 -07:00
|
|
|
let (transaction_channel, tx_recv) = mpsc::channel(self.max_nb_txs_in_queue);
|
|
|
|
let (replay_channel, replay_reciever) = tokio::sync::mpsc::unbounded_channel();
|
|
|
|
|
2023-07-20 04:45:54 -07:00
|
|
|
let jh_services: AnyhowJoinHandle = {
|
|
|
|
let tx_sender = self.tx_sender.clone();
|
|
|
|
let tx_replayer = self.tx_replayer.clone();
|
|
|
|
let tpu_service = self.tpu_service.clone();
|
|
|
|
let replay_channel_task = replay_channel.clone();
|
|
|
|
|
|
|
|
tokio::spawn(async move {
|
2023-08-31 03:34:13 -07:00
|
|
|
let tpu_service_fx = tpu_service.start(slot_notifications);
|
2023-07-20 04:45:54 -07:00
|
|
|
|
|
|
|
let tx_sender_jh = tx_sender.clone().execute(tx_recv, notifier.clone());
|
|
|
|
|
|
|
|
let replay_service =
|
|
|
|
tx_replayer.start_service(replay_channel_task, replay_reciever);
|
|
|
|
|
|
|
|
tokio::select! {
|
|
|
|
res = tpu_service_fx => {
|
|
|
|
bail!("Tpu Service {res:?}")
|
|
|
|
},
|
|
|
|
res = tx_sender_jh => {
|
|
|
|
bail!("Tx Sender {res:?}")
|
|
|
|
},
|
|
|
|
res = replay_service => {
|
|
|
|
bail!("Replay Service {res:?}")
|
|
|
|
},
|
|
|
|
}
|
|
|
|
})
|
|
|
|
};
|
2023-06-12 12:30:47 -07:00
|
|
|
|
|
|
|
(
|
|
|
|
TransactionService {
|
|
|
|
transaction_channel,
|
|
|
|
replay_channel,
|
|
|
|
block_store,
|
|
|
|
max_retries,
|
2023-09-15 02:05:36 -07:00
|
|
|
replay_offset: self.tx_replayer.retry_offset,
|
2023-06-12 12:30:47 -07:00
|
|
|
},
|
|
|
|
jh_services,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct TransactionService {
|
2023-09-20 07:57:01 -07:00
|
|
|
pub transaction_channel: Sender<SentTransactionInfo>,
|
2023-06-12 12:30:47 -07:00
|
|
|
pub replay_channel: UnboundedSender<TransactionReplay>,
|
2023-08-31 03:34:13 -07:00
|
|
|
pub block_store: BlockInformationStore,
|
2023-06-12 12:30:47 -07:00
|
|
|
pub max_retries: usize,
|
2023-09-15 02:05:36 -07:00
|
|
|
pub replay_offset: Duration,
|
2023-06-12 12:30:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl TransactionService {
|
|
|
|
pub async fn send_transaction(
|
|
|
|
&self,
|
|
|
|
raw_tx: Vec<u8>,
|
|
|
|
max_retries: Option<u16>,
|
|
|
|
) -> anyhow::Result<String> {
|
|
|
|
let tx = match bincode::deserialize::<VersionedTransaction>(&raw_tx) {
|
|
|
|
Ok(tx) => tx,
|
|
|
|
Err(err) => {
|
|
|
|
bail!(err.to_string());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let signature = tx.signatures[0];
|
|
|
|
|
2023-08-28 16:13:08 -07:00
|
|
|
let Some(BlockInformation {
|
|
|
|
slot,
|
|
|
|
last_valid_blockheight,
|
|
|
|
..
|
|
|
|
}) = self
|
2023-06-12 12:30:47 -07:00
|
|
|
.block_store
|
|
|
|
.get_block_info(&tx.get_recent_blockhash().to_string())
|
|
|
|
else {
|
|
|
|
bail!("Blockhash not found in block store".to_string());
|
|
|
|
};
|
2023-09-20 07:57:01 -07:00
|
|
|
|
2023-06-12 12:30:47 -07:00
|
|
|
let max_replay = max_retries.map_or(self.max_retries, |x| x as usize);
|
2023-09-20 07:57:01 -07:00
|
|
|
let transaction_info = SentTransactionInfo {
|
|
|
|
signature: signature.to_string(),
|
|
|
|
last_valid_block_height: last_valid_blockheight,
|
|
|
|
slot,
|
|
|
|
transaction: raw_tx,
|
|
|
|
};
|
2023-06-12 12:30:47 -07:00
|
|
|
if let Err(e) = self
|
|
|
|
.transaction_channel
|
2023-09-20 07:57:01 -07:00
|
|
|
.send(transaction_info.clone())
|
2023-06-12 12:30:47 -07:00
|
|
|
.await
|
|
|
|
{
|
|
|
|
bail!(
|
|
|
|
"Internal error sending transaction on send channel error {}",
|
|
|
|
e
|
|
|
|
);
|
|
|
|
}
|
2023-09-15 02:05:36 -07:00
|
|
|
let replay_at = Instant::now() + self.replay_offset;
|
2023-06-12 12:30:47 -07:00
|
|
|
// ignore error for replay service
|
2023-09-09 08:43:51 -07:00
|
|
|
if self
|
|
|
|
.replay_channel
|
|
|
|
.send(TransactionReplay {
|
2023-09-20 07:57:01 -07:00
|
|
|
transaction: transaction_info,
|
2023-09-09 08:43:51 -07:00
|
|
|
replay_count: 0,
|
|
|
|
max_replay,
|
|
|
|
replay_at,
|
|
|
|
})
|
|
|
|
.is_ok()
|
|
|
|
{
|
|
|
|
MESSAGES_IN_REPLAY_QUEUE.inc();
|
|
|
|
}
|
2023-06-12 12:30:47 -07:00
|
|
|
Ok(signature.to_string())
|
|
|
|
}
|
|
|
|
}
|