1pub use crate::tpu_client::Result;
2use {
3 crate::tpu_client::{MAX_FANOUT_SLOTS, RecentLeaderSlots, TpuClientConfig},
4 futures_util::{future::join_all, stream::StreamExt},
5 log::*,
6 solana_clock::{DEFAULT_MS_PER_SLOT, Slot},
7 solana_commitment_config::CommitmentConfig,
8 solana_connection_cache::{
9 connection_cache::{
10 ConnectionCache, ConnectionManager, ConnectionPool, DEFAULT_CONNECTION_POOL_SIZE,
11 NewConnectionConfig, Protocol,
12 },
13 nonblocking::client_connection::ClientConnection,
14 },
15 solana_epoch_schedule::EpochSchedule,
16 solana_leader_schedule::NUM_CONSECUTIVE_LEADER_SLOTS,
17 solana_pubkey::Pubkey,
18 solana_pubsub_client::nonblocking::pubsub_client::{PubsubClient, PubsubClientError},
19 solana_rpc_client::nonblocking::rpc_client::RpcClient,
20 solana_rpc_client_api::{
21 client_error::{Error as ClientError, ErrorKind, Result as ClientResult},
22 request::RpcError,
23 response::{RpcContactInfo, SlotUpdate},
24 },
25 solana_signer::SignerError,
26 solana_transaction::{Transaction, versioned::VersionedTransaction},
27 solana_transaction_error::{TransportError, TransportResult},
28 std::{
29 collections::{HashMap, HashSet},
30 net::SocketAddr,
31 str::FromStr,
32 sync::{
33 Arc, RwLock,
34 atomic::{AtomicBool, Ordering},
35 },
36 },
37 thiserror::Error,
38 tokio::{
39 task::JoinHandle,
40 time::{Duration, Instant, sleep, timeout},
41 },
42};
43#[cfg(feature = "spinner")]
44use {
45 crate::tpu_client::{SEND_TRANSACTION_INTERVAL, TRANSACTION_RESEND_INTERVAL},
46 futures_util::FutureExt,
47 indicatif::ProgressBar,
48 solana_message::Message,
49 solana_rpc_client::spinner::{self, SendTransactionProgress},
50 solana_rpc_client_api::request::MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS,
51 solana_signer::signers::Signers,
52 solana_transaction_error::TransactionError,
53 std::{future::Future, iter},
54};
55
56#[derive(Error, Debug)]
57pub enum TpuSenderError {
58 #[error("Pubsub error: {0:?}")]
59 PubsubError(#[from] PubsubClientError),
60 #[error("RPC error: {0:?}")]
61 RpcError(#[from] ClientError),
62 #[error("IO error: {0:?}")]
63 IoError(#[from] std::io::Error),
64 #[error("Signer error: {0:?}")]
65 SignerError(#[from] SignerError),
66 #[error("Custom error: {0}")]
67 Custom(String),
68}
69
70struct LeaderTpuCacheUpdateInfo {
71 pub(super) maybe_cluster_nodes: Option<ClientResult<Vec<RpcContactInfo>>>,
72 pub(super) maybe_epoch_schedule: Option<ClientResult<EpochSchedule>>,
73 pub(super) maybe_slot_leaders: Option<ClientResult<Vec<Pubkey>>>,
74 pub(super) first_slot: Slot,
75}
76impl LeaderTpuCacheUpdateInfo {
77 pub fn has_some(&self) -> bool {
78 self.maybe_cluster_nodes.is_some()
79 || self.maybe_epoch_schedule.is_some()
80 || self.maybe_slot_leaders.is_some()
81 }
82}
83
84struct LeaderTpuCache {
85 protocol: Protocol,
86 first_slot: Slot,
87 leaders: Vec<Pubkey>,
88 leader_tpu_map: HashMap<Pubkey, SocketAddr>,
89 slots_in_epoch: Slot,
90 last_slot_in_epoch: Slot,
91}
92
93impl LeaderTpuCache {
94 pub fn new(
95 first_slot: Slot,
96 slots_in_epoch: Slot,
97 last_slot_in_epoch: Slot,
98 leaders: Vec<Pubkey>,
99 cluster_nodes: Vec<RpcContactInfo>,
100 protocol: Protocol,
101 ) -> Self {
102 let leader_tpu_map = Self::extract_cluster_tpu_sockets(protocol, cluster_nodes);
103 Self {
104 protocol,
105 first_slot,
106 leaders,
107 leader_tpu_map,
108 slots_in_epoch,
109 last_slot_in_epoch,
110 }
111 }
112
113 pub fn last_slot(&self) -> Slot {
115 self.first_slot + self.leaders.len().saturating_sub(1) as u64
116 }
117
118 pub fn slot_info(&self) -> (Slot, Slot, Slot) {
119 (
120 self.last_slot(),
121 self.last_slot_in_epoch,
122 self.slots_in_epoch,
123 )
124 }
125
126 fn get_unique_leader_sockets(
128 &self,
129 estimated_current_slot: Slot,
130 fanout_slots: u64,
131 ) -> Vec<SocketAddr> {
132 let all_leader_sockets = self.get_leader_sockets(estimated_current_slot, fanout_slots);
133
134 let mut unique_sockets = Vec::new();
135 let mut seen = HashSet::new();
136
137 for socket in all_leader_sockets {
138 if seen.insert(socket) {
139 unique_sockets.push(socket);
140 }
141 }
142
143 unique_sockets
144 }
145
146 fn get_leader_sockets(
148 &self,
149 estimated_current_slot: Slot,
150 fanout_slots: u64,
151 ) -> Vec<SocketAddr> {
152 let mut leader_sockets = Vec::new();
153 let current_slot = std::cmp::max(estimated_current_slot, self.first_slot);
157 for leader_slot in
158 (current_slot..current_slot + fanout_slots).step_by(NUM_CONSECUTIVE_LEADER_SLOTS.get())
159 {
160 if let Some(leader) = self.get_slot_leader(leader_slot) {
161 if let Some(tpu_socket) = self.leader_tpu_map.get(leader) {
162 leader_sockets.push(*tpu_socket);
163 } else {
164 trace!("TPU not available for leader {leader}");
166 }
167 } else {
168 warn!(
170 "Leader not known for slot {}; cache holds slots [{},{}]",
171 leader_slot,
172 self.first_slot,
173 self.last_slot()
174 );
175 }
176 }
177 leader_sockets
178 }
179
180 pub fn get_slot_leader(&self, slot: Slot) -> Option<&Pubkey> {
181 if slot >= self.first_slot {
182 let index = slot - self.first_slot;
183 self.leaders.get(index as usize)
184 } else {
185 None
186 }
187 }
188
189 fn extract_cluster_tpu_sockets(
190 protocol: Protocol,
191 cluster_contact_info: Vec<RpcContactInfo>,
192 ) -> HashMap<Pubkey, SocketAddr> {
193 cluster_contact_info
194 .into_iter()
195 .filter_map(|contact_info| {
196 let pubkey = Pubkey::from_str(&contact_info.pubkey).ok()?;
197 let socket = match protocol {
198 Protocol::QUIC => contact_info.tpu_quic,
199 Protocol::UDP => contact_info.tpu,
200 }?;
201 Some((pubkey, socket))
202 })
203 .collect()
204 }
205
206 pub fn fanout(slots_in_epoch: Slot) -> Slot {
207 (2 * MAX_FANOUT_SLOTS).min(slots_in_epoch)
208 }
209
210 pub fn update_all(&mut self, cache_update_info: LeaderTpuCacheUpdateInfo) -> (bool, bool) {
211 let mut has_error = false;
212 let mut cluster_refreshed = false;
213 if let Some(cluster_nodes) = cache_update_info.maybe_cluster_nodes {
214 match cluster_nodes {
215 Ok(cluster_nodes) => {
216 self.leader_tpu_map =
217 Self::extract_cluster_tpu_sockets(self.protocol, cluster_nodes);
218 cluster_refreshed = true;
219 }
220 Err(err) => {
221 warn!("Failed to fetch cluster tpu sockets: {err}");
222 has_error = true;
223 }
224 }
225 }
226
227 if let Some(Ok(epoch_schedule)) = cache_update_info.maybe_epoch_schedule {
228 let epoch = epoch_schedule.get_epoch(cache_update_info.first_slot);
229 self.slots_in_epoch = epoch_schedule.get_slots_in_epoch(epoch);
230 self.last_slot_in_epoch = epoch_schedule.get_last_slot_in_epoch(epoch);
231 }
232
233 if let Some(slot_leaders) = cache_update_info.maybe_slot_leaders {
234 match slot_leaders {
235 Ok(slot_leaders) => {
236 self.first_slot = cache_update_info.first_slot;
237 self.leaders = slot_leaders;
238 }
239 Err(err) => {
240 warn!(
241 "Failed to fetch slot leaders (first_slot: {}): {err}",
242 cache_update_info.first_slot
243 );
244 has_error = true;
245 }
246 }
247 }
248 (has_error, cluster_refreshed)
249 }
250}
251
252pub struct TpuClient<
255 P, M, C, > {
259 fanout_slots: u64,
260 leader_tpu_service: LeaderTpuService,
261 exit: Arc<AtomicBool>,
262 rpc_client: Arc<RpcClient>,
263 connection_cache: Arc<ConnectionCache<P, M, C>>,
264}
265
266#[cfg(feature = "spinner")]
269fn send_wire_transaction_futures<'a, P, M, C>(
270 progress_bar: &'a ProgressBar,
271 progress: &'a SendTransactionProgress,
272 index: usize,
273 num_transactions: usize,
274 wire_transaction: Vec<u8>,
275 leaders: Vec<SocketAddr>,
276 connection_cache: &'a ConnectionCache<P, M, C>,
277) -> Vec<impl Future<Output = TransportResult<()>> + 'a>
278where
279 P: ConnectionPool<NewConnectionConfig = C>,
280 M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
281 C: NewConnectionConfig,
282{
283 const SEND_TIMEOUT_INTERVAL: Duration = Duration::from_secs(5);
284 let sleep_duration = SEND_TRANSACTION_INTERVAL.saturating_mul(index as u32);
285 let send_timeout = SEND_TIMEOUT_INTERVAL.saturating_add(sleep_duration);
286 leaders
287 .into_iter()
288 .map(|addr| {
289 timeout_future(
290 send_timeout,
291 sleep_and_send_wire_transaction_to_addr(
292 sleep_duration,
293 connection_cache,
294 addr,
295 wire_transaction.clone(),
296 ),
297 )
298 .boxed_local() })
300 .chain(iter::once(
301 timeout_future(
302 send_timeout,
303 sleep_and_set_message(
304 sleep_duration,
305 progress_bar,
306 progress,
307 index,
308 num_transactions,
309 ),
310 )
311 .boxed_local(), ))
313 .collect::<Vec<_>>()
314}
315
316#[cfg(feature = "spinner")]
321async fn timeout_future<Fut: Future<Output = TransportResult<()>>>(
322 timeout_duration: Duration,
323 future: Fut,
324) -> TransportResult<()> {
325 timeout(timeout_duration, future)
326 .await
327 .unwrap_or_else(|_| Err(TransportError::Custom("Timed out".to_string())))
328}
329
330#[cfg(feature = "spinner")]
331async fn sleep_and_set_message(
332 sleep_duration: Duration,
333 progress_bar: &ProgressBar,
334 progress: &SendTransactionProgress,
335 index: usize,
336 num_transactions: usize,
337) -> TransportResult<()> {
338 sleep(sleep_duration).await;
339 progress.set_message_for_confirmed_transactions(
340 progress_bar,
341 &format!("Sending {}/{} transactions", index + 1, num_transactions,),
342 );
343 Ok(())
344}
345
346#[cfg(feature = "spinner")]
347async fn sleep_and_send_wire_transaction_to_addr<P, M, C>(
348 sleep_duration: Duration,
349 connection_cache: &ConnectionCache<P, M, C>,
350 addr: SocketAddr,
351 wire_transaction: Vec<u8>,
352) -> TransportResult<()>
353where
354 P: ConnectionPool<NewConnectionConfig = C>,
355 M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
356 C: NewConnectionConfig,
357{
358 sleep(sleep_duration).await;
359 send_wire_transaction_to_addr(connection_cache, &addr, wire_transaction).await
360}
361
362async fn send_wire_transaction_to_addr<P, M, C>(
363 connection_cache: &ConnectionCache<P, M, C>,
364 addr: &SocketAddr,
365 wire_transaction: Vec<u8>,
366) -> TransportResult<()>
367where
368 P: ConnectionPool<NewConnectionConfig = C>,
369 M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
370 C: NewConnectionConfig,
371{
372 let conn = connection_cache.get_nonblocking_connection(addr);
373 conn.send_data(&wire_transaction).await
374}
375
376async fn send_wire_transaction_batch_to_addr<P, M, C>(
377 connection_cache: &ConnectionCache<P, M, C>,
378 addr: &SocketAddr,
379 wire_transactions: &[Vec<u8>],
380) -> TransportResult<()>
381where
382 P: ConnectionPool<NewConnectionConfig = C>,
383 M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
384 C: NewConnectionConfig,
385{
386 let conn = connection_cache.get_nonblocking_connection(addr);
387 conn.send_data_batch(wire_transactions).await
388}
389
390impl<P, M, C> TpuClient<P, M, C>
391where
392 P: ConnectionPool<NewConnectionConfig = C>,
393 M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
394 C: NewConnectionConfig,
395{
396 pub async fn send_transaction(&self, transaction: &Transaction) -> bool {
399 let wire_transaction =
400 wincode::serialize(transaction).expect("serialization should succeed");
401 self.send_wire_transaction(wire_transaction).await
402 }
403
404 pub async fn send_wire_transaction(&self, wire_transaction: Vec<u8>) -> bool {
406 self.try_send_wire_transaction(wire_transaction)
407 .await
408 .is_ok()
409 }
410
411 pub async fn try_send_transaction(
415 &self,
416 transaction: &VersionedTransaction,
417 ) -> TransportResult<()> {
418 let wire_transaction =
419 wincode::serialize(transaction).expect("serialization should succeed");
420 self.try_send_wire_transaction(wire_transaction).await
421 }
422
423 pub async fn try_send_wire_transaction(
426 &self,
427 wire_transaction: Vec<u8>,
428 ) -> TransportResult<()> {
429 let leaders = self
430 .leader_tpu_service
431 .unique_leader_tpu_sockets(self.fanout_slots);
432 let futures = leaders
433 .iter()
434 .map(|addr| {
435 send_wire_transaction_to_addr(
436 &self.connection_cache,
437 addr,
438 wire_transaction.clone(),
439 )
440 })
441 .collect::<Vec<_>>();
442 let results: Vec<TransportResult<()>> = join_all(futures).await;
443
444 let mut last_error: Option<TransportError> = None;
445 let mut some_success = false;
446 for result in results {
447 if let Err(e) = result {
448 if last_error.is_none() {
449 last_error = Some(e);
450 }
451 } else {
452 some_success = true;
453 }
454 }
455 if !some_success {
456 Err(if let Some(err) = last_error {
457 err
458 } else {
459 std::io::Error::other("No sends attempted").into()
460 })
461 } else {
462 Ok(())
463 }
464 }
465
466 pub async fn try_send_wire_transaction_batch(
470 &self,
471 wire_transactions: Vec<Vec<u8>>,
472 ) -> TransportResult<()> {
473 let leaders = self
474 .leader_tpu_service
475 .unique_leader_tpu_sockets(self.fanout_slots);
476 let futures = leaders
477 .iter()
478 .map(|addr| {
479 send_wire_transaction_batch_to_addr(
480 &self.connection_cache,
481 addr,
482 &wire_transactions,
483 )
484 })
485 .collect::<Vec<_>>();
486 let results: Vec<TransportResult<()>> = join_all(futures).await;
487
488 let mut last_error: Option<TransportError> = None;
489 let mut some_success = false;
490 for result in results {
491 if let Err(e) = result {
492 if last_error.is_none() {
493 last_error = Some(e);
494 }
495 } else {
496 some_success = true;
497 }
498 }
499 if !some_success {
500 Err(if let Some(err) = last_error {
501 err
502 } else {
503 std::io::Error::other("No sends attempted").into()
504 })
505 } else {
506 Ok(())
507 }
508 }
509
510 pub async fn new(
512 name: &'static str,
513 rpc_client: Arc<RpcClient>,
514 websocket_url: &str,
515 config: TpuClientConfig,
516 connection_manager: M,
517 ) -> Result<Self> {
518 let connection_cache = Arc::new(
519 ConnectionCache::new(name, connection_manager, DEFAULT_CONNECTION_POOL_SIZE).unwrap(),
520 ); Self::new_with_connection_cache(rpc_client, websocket_url, config, connection_cache).await
522 }
523
524 pub async fn new_with_connection_cache(
526 rpc_client: Arc<RpcClient>,
527 websocket_url: &str,
528 config: TpuClientConfig,
529 connection_cache: Arc<ConnectionCache<P, M, C>>,
530 ) -> Result<Self> {
531 let exit = Arc::new(AtomicBool::new(false));
532 let leader_tpu_service =
533 LeaderTpuService::new(rpc_client.clone(), websocket_url, M::PROTOCOL, exit.clone())
534 .await?;
535
536 Ok(Self {
537 fanout_slots: config.fanout_slots.clamp(1, MAX_FANOUT_SLOTS),
538 leader_tpu_service,
539 exit,
540 rpc_client,
541 connection_cache,
542 })
543 }
544
545 #[deprecated(
546 since = "4.3.0",
547 note = "prefer solana_client::send_and_confirm_transactions_in_parallel_v3"
548 )]
549 #[cfg(feature = "spinner")]
550 pub async fn send_and_confirm_messages_with_spinner<T: Signers + ?Sized>(
551 &self,
552 messages: &[Message],
553 signers: &T,
554 ) -> Result<Vec<Option<TransactionError>>> {
555 let mut progress = SendTransactionProgress::default();
556 let progress_bar = spinner::new_progress_bar();
557 progress_bar.set_message("Setting up...");
558
559 let mut transactions = messages
560 .iter()
561 .enumerate()
562 .map(|(i, message)| (i, Transaction::new_unsigned(message.clone())))
563 .collect::<Vec<_>>();
564 progress.total_transactions = transactions.len();
565 let mut transaction_errors = vec![None; transactions.len()];
566 progress.block_height = self.rpc_client.get_block_height().await?;
567 for expired_blockhash_retries in (0..5).rev() {
568 let (blockhash, last_valid_block_height) = self
569 .rpc_client
570 .get_latest_blockhash_with_commitment(self.rpc_client.commitment())
571 .await?;
572 progress.last_valid_block_height = last_valid_block_height;
573
574 let mut pending_transactions = HashMap::new();
575 for (i, mut transaction) in transactions {
576 transaction.try_sign(signers, blockhash)?;
577 pending_transactions.insert(transaction.signatures[0], (i, transaction));
578 }
579
580 let mut last_resend = Instant::now() - TRANSACTION_RESEND_INTERVAL;
581 while progress.block_height <= progress.last_valid_block_height {
582 let num_transactions = pending_transactions.len();
583
584 if Instant::now().duration_since(last_resend) > TRANSACTION_RESEND_INTERVAL {
586 let mut futures = vec![];
588 for (index, (_i, transaction)) in pending_transactions.values().enumerate() {
589 let wire_transaction = wincode::serialize(transaction).unwrap();
590 let leaders = self
591 .leader_tpu_service
592 .unique_leader_tpu_sockets(self.fanout_slots);
593 futures.extend(send_wire_transaction_futures(
594 &progress_bar,
595 &progress,
596 index,
597 num_transactions,
598 wire_transaction,
599 leaders,
600 &self.connection_cache,
601 ));
602 }
603
604 let results = join_all(futures).await;
606
607 progress.set_message_for_confirmed_transactions(
608 &progress_bar,
609 "Checking sent transactions",
610 );
611 for (index, (tx_results, (_i, transaction))) in results
612 .chunks(self.fanout_slots as usize)
613 .zip(pending_transactions.values())
614 .enumerate()
615 {
616 if tx_results.iter().all(|r| r.is_err()) {
618 progress.set_message_for_confirmed_transactions(
619 &progress_bar,
620 &format!(
621 "Resending failed transaction {} of {}",
622 index + 1,
623 num_transactions,
624 ),
625 );
626 let _result = self.rpc_client.send_transaction(transaction).await.ok();
627 }
628 }
629 last_resend = Instant::now();
630 }
631
632 let mut block_height_refreshes = 10;
634 progress.set_message_for_confirmed_transactions(
635 &progress_bar,
636 &format!("Waiting for next block, {num_transactions} transactions pending..."),
637 );
638 let mut new_block_height = progress.block_height;
639 while progress.block_height == new_block_height && block_height_refreshes > 0 {
640 sleep(Duration::from_millis(500)).await;
641 new_block_height = self.rpc_client.get_block_height().await?;
642 block_height_refreshes -= 1;
643 }
644 progress.block_height = new_block_height;
645
646 let pending_signatures = pending_transactions.keys().cloned().collect::<Vec<_>>();
648 for pending_signatures_chunk in
649 pending_signatures.chunks(MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS)
650 {
651 if let Ok(result) = self
652 .rpc_client
653 .get_signature_statuses(pending_signatures_chunk)
654 .await
655 {
656 let statuses = result.value;
657 for (signature, status) in pending_signatures_chunk.iter().zip(statuses) {
658 if let Some(status) = status
659 && status.satisfies_commitment(self.rpc_client.commitment())
660 && let Some((i, _)) = pending_transactions.remove(signature)
661 {
662 progress.confirmed_transactions += 1;
663 if status.err.is_some() {
664 progress_bar.println(format!("Failed transaction: {status:?}"));
665 }
666 transaction_errors[i] = status.err;
667 }
668 }
669 }
670 progress.set_message_for_confirmed_transactions(
671 &progress_bar,
672 "Checking transaction status...",
673 );
674 }
675
676 if pending_transactions.is_empty() {
677 return Ok(transaction_errors);
678 }
679 }
680
681 transactions = pending_transactions.into_values().collect();
682 progress_bar.println(format!(
683 "Blockhash expired. {expired_blockhash_retries} retries remaining"
684 ));
685 }
686 Err(TpuSenderError::Custom("Max retries exceeded".into()))
687 }
688
689 pub fn rpc_client(&self) -> &RpcClient {
690 &self.rpc_client
691 }
692
693 pub async fn shutdown(&mut self) {
694 self.exit.store(true, Ordering::Relaxed);
695 self.leader_tpu_service.join().await;
696 }
697
698 pub fn get_connection_cache(&self) -> &Arc<ConnectionCache<P, M, C>>
699 where
700 P: ConnectionPool<NewConnectionConfig = C>,
701 M: ConnectionManager<ConnectionPool = P, NewConnectionConfig = C>,
702 C: NewConnectionConfig,
703 {
704 &self.connection_cache
705 }
706
707 pub fn get_leader_tpu_service(&self) -> &LeaderTpuService {
708 &self.leader_tpu_service
709 }
710
711 pub fn get_fanout_slots(&self) -> u64 {
712 self.fanout_slots
713 }
714}
715
716impl<P, M, C> Drop for TpuClient<P, M, C> {
717 fn drop(&mut self) {
718 self.exit.store(true, Ordering::Relaxed);
719 }
720}
721
722pub struct LeaderTpuService {
725 recent_slots: RecentLeaderSlots,
726 leader_tpu_cache: Arc<RwLock<LeaderTpuCache>>,
727 t_leader_tpu_service: Option<JoinHandle<Result<()>>>,
728}
729
730impl LeaderTpuService {
731 pub async fn new(
732 rpc_client: Arc<RpcClient>,
733 websocket_url: &str,
734 protocol: Protocol,
735 exit: Arc<AtomicBool>,
736 ) -> Result<Self> {
737 let epoch_schedule = rpc_client.get_epoch_schedule().await?;
738 let start_slot = rpc_client
739 .get_slot_with_commitment(CommitmentConfig::processed())
740 .await?;
741
742 let recent_slots = RecentLeaderSlots::new(start_slot);
743 let epoch = epoch_schedule.get_epoch(start_slot);
744 let slots_in_epoch = epoch_schedule.get_slots_in_epoch(epoch);
745 let last_slot_in_epoch = epoch_schedule.get_last_slot_in_epoch(epoch);
746
747 let tpu_leader_service_creation_timeout = Duration::from_secs(20);
751 let retry_interval = Duration::from_secs(1);
752 let leaders = timeout(tpu_leader_service_creation_timeout, async {
753 loop {
754 match rpc_client
759 .get_slot_leaders(start_slot, LeaderTpuCache::fanout(slots_in_epoch))
760 .await
761 {
762 Ok(leaders) => return Ok(leaders),
763 Err(client_error) => {
764 if is_invalid_slot_range_error(&client_error) {
765 sleep(retry_interval).await;
766 continue;
767 } else {
768 return Err(client_error);
769 }
770 }
771 }
772 }
773 })
774 .await
775 .map_err(|_| {
776 TpuSenderError::Custom(format!(
777 "Failed to get slot leaders connecting to: {websocket_url}, timeout: \
778 {tpu_leader_service_creation_timeout:?}. Invalid slot range"
779 ))
780 })??;
781
782 let cluster_nodes = timeout(tpu_leader_service_creation_timeout, async {
783 loop {
784 let cluster_nodes = rpc_client.get_cluster_nodes().await?;
785 if cluster_nodes.iter().any(|rpc_contact_info| {
787 Pubkey::from_str(&rpc_contact_info.pubkey)
788 .map(|pubkey| leaders.contains(&pubkey))
789 .unwrap_or(false)
790 }) {
791 return Ok::<_, ClientError>(cluster_nodes);
792 }
793 sleep(retry_interval).await;
794 }
795 })
796 .await
797 .map_err(|_| {
798 TpuSenderError::Custom(format!(
799 "Failed find any cluster node info for upcoming leaders, timeout: \
800 {tpu_leader_service_creation_timeout:?}."
801 ))
802 })??;
803 let leader_tpu_cache = Arc::new(RwLock::new(LeaderTpuCache::new(
804 start_slot,
805 slots_in_epoch,
806 last_slot_in_epoch,
807 leaders,
808 cluster_nodes,
809 protocol,
810 )));
811
812 let pubsub_client = if !websocket_url.is_empty() {
813 Some(PubsubClient::new(websocket_url).await?)
814 } else {
815 None
816 };
817
818 let t_leader_tpu_service = Some({
819 let recent_slots = recent_slots.clone();
820 let leader_tpu_cache = leader_tpu_cache.clone();
821 tokio::spawn(Self::run(
822 rpc_client,
823 recent_slots,
824 leader_tpu_cache,
825 pubsub_client,
826 exit,
827 ))
828 });
829
830 Ok(LeaderTpuService {
831 recent_slots,
832 leader_tpu_cache,
833 t_leader_tpu_service,
834 })
835 }
836
837 pub async fn join(&mut self) {
838 if let Some(t_handle) = self.t_leader_tpu_service.take() {
839 t_handle.await.unwrap().unwrap();
840 }
841 }
842
843 pub fn estimated_current_slot(&self) -> Slot {
844 self.recent_slots.estimated_current_slot()
845 }
846
847 pub fn unique_leader_tpu_sockets(&self, fanout_slots: u64) -> Vec<SocketAddr> {
848 let current_slot = self.recent_slots.estimated_current_slot();
849 self.leader_tpu_cache
850 .read()
851 .unwrap()
852 .get_unique_leader_sockets(current_slot, fanout_slots)
853 }
854
855 pub fn leader_tpu_sockets(&self, fanout_slots: u64) -> Vec<SocketAddr> {
856 let current_slot = self.recent_slots.estimated_current_slot();
857 self.leader_tpu_cache
858 .read()
859 .unwrap()
860 .get_leader_sockets(current_slot, fanout_slots)
861 }
862
863 async fn run(
864 rpc_client: Arc<RpcClient>,
865 recent_slots: RecentLeaderSlots,
866 leader_tpu_cache: Arc<RwLock<LeaderTpuCache>>,
867 pubsub_client: Option<PubsubClient>,
868 exit: Arc<AtomicBool>,
869 ) -> Result<()> {
870 tokio::try_join!(
871 Self::run_slot_watcher(recent_slots.clone(), pubsub_client, exit.clone()),
872 Self::run_cache_refresher(rpc_client, recent_slots, leader_tpu_cache, exit),
873 )?;
874
875 Ok(())
876 }
877
878 async fn run_cache_refresher(
879 rpc_client: Arc<RpcClient>,
880 recent_slots: RecentLeaderSlots,
881 leader_tpu_cache: Arc<RwLock<LeaderTpuCache>>,
882 exit: Arc<AtomicBool>,
883 ) -> Result<()> {
884 let mut last_cluster_refresh = Instant::now();
885 let mut sleep_ms = DEFAULT_MS_PER_SLOT;
886
887 while !exit.load(Ordering::Relaxed) {
888 sleep(Duration::from_millis(sleep_ms)).await;
890 sleep_ms = DEFAULT_MS_PER_SLOT;
891
892 let cache_update_info = maybe_fetch_cache_info(
893 &leader_tpu_cache,
894 last_cluster_refresh,
895 &rpc_client,
896 &recent_slots,
897 )
898 .await;
899
900 if cache_update_info.has_some() {
901 let mut leader_tpu_cache = leader_tpu_cache.write().unwrap();
902 let (has_error, cluster_refreshed) = leader_tpu_cache.update_all(cache_update_info);
903 if has_error {
904 sleep_ms = 100;
905 }
906 if cluster_refreshed {
907 last_cluster_refresh = Instant::now();
908 }
909 }
910 }
911
912 Ok(())
913 }
914
915 async fn run_slot_watcher(
916 recent_slots: RecentLeaderSlots,
917 pubsub_client: Option<PubsubClient>,
918 exit: Arc<AtomicBool>,
919 ) -> Result<()> {
920 let Some(pubsub_client) = pubsub_client else {
921 return Ok(());
922 };
923
924 let (mut notifications, unsubscribe) = pubsub_client.slot_updates_subscribe().await?;
925 const SLOT_UPDATE_TIMEOUT: Duration = Duration::from_millis(10);
936
937 while !exit.load(Ordering::Relaxed) {
938 while let Ok(Some(update)) = timeout(SLOT_UPDATE_TIMEOUT, notifications.next()).await {
939 let current_slot = match update {
940 SlotUpdate::Completed { slot, .. } => slot.saturating_add(1),
943 SlotUpdate::FirstShredReceived { slot, .. } => slot,
946 _ => continue,
947 };
948 recent_slots.record_slot(current_slot);
949 }
950 }
951
952 drop(notifications);
955 unsubscribe().await;
956 pubsub_client.shutdown().await?;
957
958 Ok(())
959 }
960}
961
962async fn maybe_fetch_cache_info(
963 leader_tpu_cache: &Arc<RwLock<LeaderTpuCache>>,
964 last_cluster_refresh: Instant,
965 rpc_client: &RpcClient,
966 recent_slots: &RecentLeaderSlots,
967) -> LeaderTpuCacheUpdateInfo {
968 let maybe_cluster_nodes = if last_cluster_refresh.elapsed() > Duration::from_secs(5 * 60) {
971 Some(rpc_client.get_cluster_nodes().await)
972 } else {
973 None
974 };
975
976 let estimated_current_slot = recent_slots.estimated_current_slot();
978 let (last_slot, last_slot_in_epoch, slots_in_epoch) = {
979 let leader_tpu_cache = leader_tpu_cache.read().unwrap();
980 leader_tpu_cache.slot_info()
981 };
982
983 let maybe_epoch_schedule = if estimated_current_slot > last_slot_in_epoch {
985 Some(rpc_client.get_epoch_schedule().await)
986 } else {
987 None
988 };
989
990 let maybe_slot_leaders = if estimated_current_slot >= last_slot.saturating_sub(MAX_FANOUT_SLOTS)
995 {
996 Some(
997 rpc_client
998 .get_slot_leaders(
999 estimated_current_slot,
1000 LeaderTpuCache::fanout(slots_in_epoch),
1001 )
1002 .await,
1003 )
1004 } else {
1005 None
1006 };
1007 LeaderTpuCacheUpdateInfo {
1008 maybe_cluster_nodes,
1009 maybe_epoch_schedule,
1010 maybe_slot_leaders,
1011 first_slot: estimated_current_slot,
1012 }
1013}
1014
1015fn is_invalid_slot_range_error(client_error: &ClientError) -> bool {
1016 if let ErrorKind::RpcError(RpcError::RpcResponseError { code, message, .. }) =
1017 client_error.kind()
1018 {
1019 return *code == -32602
1020 && message.contains("Invalid slot range: leader schedule for epoch");
1021 }
1022 false
1023}