1use {
5 async_lock::Mutex,
6 async_trait::async_trait,
7 futures::future::TryFutureExt,
8 log::*,
9 quinn::{
10 ClientConfig, ClosedStream, ConnectError, Connection, ConnectionError, Endpoint,
11 EndpointConfig, IdleTimeout, TokioRuntime, TransportConfig, WriteError,
12 crypto::rustls::QuicClientConfig,
13 },
14 solana_connection_cache::{
15 client_connection::ClientStats, connection_cache_stats::ConnectionCacheStats,
16 nonblocking::client_connection::ClientConnection,
17 },
18 solana_keypair::Keypair,
19 solana_measure::measure::Measure,
20 solana_net_utils::sockets,
21 solana_rpc_client_api::client_error::ErrorKind as ClientErrorKind,
22 solana_streamer::{nonblocking::quic::ALPN_TPU_PROTOCOL_ID, quic::QUIC_MAX_TIMEOUT},
23 solana_tls_utils::{
24 QuicClientCertificate, new_dummy_x509_certificate, socket_addr_to_quic_server_name,
25 tls_client_config_builder,
26 },
27 solana_transaction_error::TransportResult,
28 std::{
29 net::{SocketAddr, UdpSocket},
30 sync::{Arc, atomic::Ordering},
31 thread,
32 time::Duration,
33 },
34 thiserror::Error,
35 tokio::{sync::OnceCell, time::timeout},
36};
37
38const QUIC_KEEP_ALIVE: Duration = Duration::from_secs(1);
39
40pub const QUIC_CONNECTION_HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(60);
44
45pub struct QuicLazyInitializedEndpoint {
47 endpoint: OnceCell<Arc<Endpoint>>,
48 client_certificate: Arc<QuicClientCertificate>,
49 client_endpoint: Option<Endpoint>,
50}
51
52#[derive(Error, Debug)]
53pub enum QuicError {
54 #[error(transparent)]
55 WriteError(#[from] WriteError),
56 #[error(transparent)]
57 ConnectionError(#[from] ConnectionError),
58 #[error(transparent)]
59 ConnectError(#[from] ConnectError),
60 #[error(transparent)]
61 ClosedStream(#[from] ClosedStream),
62}
63
64impl From<QuicError> for ClientErrorKind {
65 fn from(quic_error: QuicError) -> Self {
66 Self::Custom(format!("{quic_error:?}"))
67 }
68}
69
70impl QuicLazyInitializedEndpoint {
71 pub fn new(
72 client_certificate: Arc<QuicClientCertificate>,
73 client_endpoint: Option<Endpoint>,
74 ) -> Self {
75 Self {
76 endpoint: OnceCell::<Arc<Endpoint>>::new(),
77 client_certificate,
78 client_endpoint,
79 }
80 }
81
82 fn create_endpoint(&self) -> Endpoint {
83 let mut endpoint = if let Some(endpoint) = &self.client_endpoint {
84 endpoint.clone()
85 } else {
86 let client_socket = sockets::bind_in_range_with_config(
89 std::net::IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED),
90 solana_net_utils::VALIDATOR_PORT_RANGE,
91 sockets::SocketConfiguration::default(),
92 )
93 .expect("QuicLazyInitializedEndpoint::create_endpoint bind_in_range")
94 .1;
95 info!("Local endpoint is : {client_socket:?}");
96
97 QuicNewConnection::create_endpoint(EndpointConfig::default(), client_socket)
98 };
99
100 let mut crypto = tls_client_config_builder()
101 .with_client_auth_cert(
102 vec![self.client_certificate.certificate.clone()],
103 self.client_certificate.key.clone_key(),
104 )
105 .expect("Failed to set QUIC client certificates");
106 crypto.enable_early_data = true;
107 crypto.alpn_protocols = vec![ALPN_TPU_PROTOCOL_ID.to_vec()];
108
109 let mut config = ClientConfig::new(Arc::new(QuicClientConfig::try_from(crypto).unwrap()));
110 let mut transport_config = TransportConfig::default();
111
112 let timeout = IdleTimeout::try_from(QUIC_MAX_TIMEOUT).unwrap();
113 transport_config.max_idle_timeout(Some(timeout));
114 transport_config.keep_alive_interval(Some(QUIC_KEEP_ALIVE));
115 transport_config.send_fairness(false);
116 config.transport_config(Arc::new(transport_config));
117
118 endpoint.set_default_client_config(config);
119
120 endpoint
121 }
122
123 async fn get_endpoint(&self) -> Arc<Endpoint> {
124 self.endpoint
125 .get_or_init(|| async { Arc::new(self.create_endpoint()) })
126 .await
127 .clone()
128 }
129}
130
131impl Default for QuicLazyInitializedEndpoint {
132 fn default() -> Self {
133 let (cert, priv_key) = new_dummy_x509_certificate(&Keypair::new());
134 Self::new(
135 Arc::new(QuicClientCertificate {
136 certificate: cert,
137 key: priv_key,
138 }),
139 None,
140 )
141 }
142}
143
144#[derive(Clone)]
147struct QuicNewConnection {
148 endpoint: Arc<Endpoint>,
149 connection: Arc<Connection>,
150}
151
152impl QuicNewConnection {
153 async fn make_connection(
155 endpoint: Arc<QuicLazyInitializedEndpoint>,
156 addr: SocketAddr,
157 stats: &ClientStats,
158 ) -> Result<Self, QuicError> {
159 let mut make_connection_measure = Measure::start("make_connection_measure");
160 let endpoint = endpoint.get_endpoint().await;
161 let server_name = socket_addr_to_quic_server_name(addr);
162 let connecting = endpoint.connect(addr, &server_name)?;
163 stats.total_connections.fetch_add(1, Ordering::Relaxed);
164 if let Ok(connecting_result) = timeout(QUIC_CONNECTION_HANDSHAKE_TIMEOUT, connecting).await
165 {
166 if connecting_result.is_err() {
167 stats.connection_errors.fetch_add(1, Ordering::Relaxed);
168 }
169 make_connection_measure.stop();
170 stats
171 .make_connection_ms
172 .fetch_add(make_connection_measure.as_ms(), Ordering::Relaxed);
173
174 let connection = connecting_result?;
175
176 Ok(Self {
177 endpoint,
178 connection: Arc::new(connection),
179 })
180 } else {
181 Err(ConnectionError::TimedOut.into())
182 }
183 }
184
185 fn create_endpoint(config: EndpointConfig, client_socket: UdpSocket) -> Endpoint {
186 quinn::Endpoint::new(config, None, client_socket, Arc::new(TokioRuntime))
187 .expect("QuicNewConnection::create_endpoint quinn::Endpoint::new")
188 }
189
190 async fn make_connection_0rtt(
193 &mut self,
194 addr: SocketAddr,
195 stats: &ClientStats,
196 ) -> Result<Arc<Connection>, QuicError> {
197 let server_name = socket_addr_to_quic_server_name(addr);
198 let connecting = self.endpoint.connect(addr, &server_name)?;
199 stats.total_connections.fetch_add(1, Ordering::Relaxed);
200 let connection = match connecting.into_0rtt() {
201 Ok((connection, zero_rtt)) => {
202 if let Ok(zero_rtt) = timeout(QUIC_CONNECTION_HANDSHAKE_TIMEOUT, zero_rtt).await {
203 if zero_rtt {
204 stats.zero_rtt_accepts.fetch_add(1, Ordering::Relaxed);
205 } else {
206 stats.zero_rtt_rejects.fetch_add(1, Ordering::Relaxed);
207 }
208 connection
209 } else {
210 return Err(ConnectionError::TimedOut.into());
211 }
212 }
213 Err(connecting) => {
214 stats.connection_errors.fetch_add(1, Ordering::Relaxed);
215
216 if let Ok(connecting_result) =
217 timeout(QUIC_CONNECTION_HANDSHAKE_TIMEOUT, connecting).await
218 {
219 connecting_result?
220 } else {
221 return Err(ConnectionError::TimedOut.into());
222 }
223 }
224 };
225 self.connection = Arc::new(connection);
226 Ok(self.connection.clone())
227 }
228}
229
230pub struct QuicClient {
231 endpoint: Arc<QuicLazyInitializedEndpoint>,
232 connection: Arc<Mutex<Option<QuicNewConnection>>>,
233 addr: SocketAddr,
234 stats: Arc<ClientStats>,
235}
236
237const CONNECTION_CLOSE_CODE_APPLICATION_CLOSE: u32 = 0u32;
238const CONNECTION_CLOSE_REASON_APPLICATION_CLOSE: &[u8] = b"dropped";
239
240impl QuicClient {
241 pub async fn close(&self) {
243 let mut conn_guard = self.connection.lock().await;
244 if let Some(conn) = conn_guard.take() {
245 debug!(
246 "Closing connection to {} connection_id: {:?}",
247 self.addr, conn.connection
248 );
249 conn.connection.close(
250 CONNECTION_CLOSE_CODE_APPLICATION_CLOSE.into(),
251 CONNECTION_CLOSE_REASON_APPLICATION_CLOSE,
252 );
253 }
254 }
255}
256
257impl QuicClient {
258 pub fn new(endpoint: Arc<QuicLazyInitializedEndpoint>, addr: SocketAddr) -> Self {
259 Self {
260 endpoint,
261 connection: Arc::new(Mutex::new(None)),
262 addr,
263 stats: Arc::new(ClientStats::default()),
264 }
265 }
266
267 async fn _send_buffer_using_conn(
268 data: &[u8],
269 connection: &Connection,
270 ) -> Result<(), QuicError> {
271 let mut send_stream = connection.open_uni().await?;
272 send_stream.write_all(data).await?;
273 Ok(())
274 }
275
276 async fn _send_buffer(
279 &self,
280 data: &[u8],
281 stats: &ClientStats,
282 connection_stats: Arc<ConnectionCacheStats>,
283 ) -> Result<Arc<Connection>, QuicError> {
284 let mut measure_send_packet = Measure::start("send_packet_us");
285 let mut measure_prepare_connection = Measure::start("prepare_connection");
286 let mut connection_try_count = 0;
287 let mut last_connection_id = 0;
288 let mut last_error = None;
289 while connection_try_count < 2 {
290 let connection = {
291 let mut conn_guard = self.connection.lock().await;
292
293 let maybe_conn = conn_guard.as_mut();
294 match maybe_conn {
295 Some(conn) => {
296 if conn.connection.stable_id() == last_connection_id {
297 let conn = conn.make_connection_0rtt(self.addr, stats).await;
299 match conn {
300 Ok(conn) => {
301 info!(
302 "Made 0rtt connection to {} with id {} try_count {}, \
303 last_connection_id: {}, last_error: {:?}",
304 self.addr,
305 conn.stable_id(),
306 connection_try_count,
307 last_connection_id,
308 last_error,
309 );
310 connection_try_count += 1;
311 conn
312 }
313 Err(err) => {
314 info!(
315 "Cannot make 0rtt connection to {}, error {:}",
316 self.addr, err
317 );
318 return Err(err);
319 }
320 }
321 } else {
322 stats.connection_reuse.fetch_add(1, Ordering::Relaxed);
323 conn.connection.clone()
324 }
325 }
326 None => {
327 let conn = QuicNewConnection::make_connection(
328 self.endpoint.clone(),
329 self.addr,
330 stats,
331 )
332 .await;
333 match conn {
334 Ok(conn) => {
335 *conn_guard = Some(conn.clone());
336 info!(
337 "Made connection to {} id {} try_count {}, from connection \
338 cache warming?: {}",
339 self.addr,
340 conn.connection.stable_id(),
341 connection_try_count,
342 data.is_empty(),
343 );
344 connection_try_count += 1;
345 conn.connection.clone()
346 }
347 Err(err) => {
348 info!(
349 "Cannot make connection to {}, error {:}, from connection \
350 cache warming?: {}",
351 self.addr,
352 err,
353 data.is_empty()
354 );
355 return Err(err);
356 }
357 }
358 }
359 }
360 };
361
362 let new_stats = connection.stats();
363
364 connection_stats
365 .total_client_stats
366 .congestion_events
367 .update_stat(
368 &self.stats.congestion_events,
369 new_stats.path.congestion_events,
370 );
371
372 connection_stats
373 .total_client_stats
374 .streams_blocked_uni
375 .update_stat(
376 &self.stats.streams_blocked_uni,
377 new_stats.frame_tx.streams_blocked_uni,
378 );
379
380 connection_stats
381 .total_client_stats
382 .data_blocked
383 .update_stat(&self.stats.data_blocked, new_stats.frame_tx.data_blocked);
384
385 connection_stats
386 .total_client_stats
387 .acks
388 .update_stat(&self.stats.acks, new_stats.frame_tx.acks);
389
390 if data.is_empty() {
391 return Ok(connection);
393 }
394
395 last_connection_id = connection.stable_id();
396 measure_prepare_connection.stop();
397
398 match Self::_send_buffer_using_conn(data, &connection).await {
399 Ok(()) => {
400 measure_send_packet.stop();
401 stats.successful_packets.fetch_add(1, Ordering::Relaxed);
402 stats
403 .send_packets_us
404 .fetch_add(measure_send_packet.as_us(), Ordering::Relaxed);
405 stats
406 .prepare_connection_us
407 .fetch_add(measure_prepare_connection.as_us(), Ordering::Relaxed);
408 trace!(
409 "Successfully sent to {} with id {}, thread: {:?}, data len: {}, \
410 send_packet_us: {} prepare_connection_us: {}",
411 self.addr,
412 connection.stable_id(),
413 thread::current().id(),
414 data.len(),
415 measure_send_packet.as_us(),
416 measure_prepare_connection.as_us(),
417 );
418
419 return Ok(connection);
420 }
421 Err(err) => match err {
422 QuicError::ConnectionError(_) => {
423 last_error = Some(err);
424 }
425 _ => {
426 info!(
427 "Error sending to {} with id {}, error {:?} thread: {:?}",
428 self.addr,
429 connection.stable_id(),
430 err,
431 thread::current().id(),
432 );
433 return Err(err);
434 }
435 },
436 }
437 }
438
439 info!(
441 "Ran into an error sending data {:?}, exhausted retries to {}",
442 last_error, self.addr
443 );
444 Err(last_error.expect("QuicClient::_send_buffer last_error.expect"))
447 }
448
449 pub async fn send_buffer<T>(
450 &self,
451 data: T,
452 stats: &ClientStats,
453 connection_stats: Arc<ConnectionCacheStats>,
454 ) -> Result<(), ClientErrorKind>
455 where
456 T: AsRef<[u8]>,
457 {
458 self._send_buffer(data.as_ref(), stats, connection_stats)
459 .await
460 .map_err(Into::<ClientErrorKind>::into)?;
461 Ok(())
462 }
463
464 pub async fn send_batch<T>(
465 &self,
466 buffers: &[T],
467 stats: &ClientStats,
468 connection_stats: Arc<ConnectionCacheStats>,
469 ) -> Result<(), ClientErrorKind>
470 where
471 T: AsRef<[u8]>,
472 {
473 if buffers.is_empty() {
485 return Ok(());
486 }
487 let connection = self
488 ._send_buffer(buffers[0].as_ref(), stats, connection_stats)
489 .await
490 .map_err(Into::<ClientErrorKind>::into)?;
491
492 for data in buffers[1..buffers.len()].iter() {
493 Self::_send_buffer_using_conn(data.as_ref(), &connection).await?;
494 }
495 Ok(())
496 }
497
498 pub fn server_addr(&self) -> &SocketAddr {
499 &self.addr
500 }
501
502 pub fn stats(&self) -> Arc<ClientStats> {
503 self.stats.clone()
504 }
505}
506
507pub struct QuicClientConnection {
508 pub client: Arc<QuicClient>,
509 pub connection_stats: Arc<ConnectionCacheStats>,
510}
511
512impl QuicClientConnection {
513 pub fn base_stats(&self) -> Arc<ClientStats> {
514 self.client.stats()
515 }
516
517 pub fn connection_stats(&self) -> Arc<ConnectionCacheStats> {
518 self.connection_stats.clone()
519 }
520
521 pub fn new(
522 endpoint: Arc<QuicLazyInitializedEndpoint>,
523 addr: SocketAddr,
524 connection_stats: Arc<ConnectionCacheStats>,
525 ) -> Self {
526 let client = Arc::new(QuicClient::new(endpoint, addr));
527 Self::new_with_client(client, connection_stats)
528 }
529
530 pub fn new_with_client(
531 client: Arc<QuicClient>,
532 connection_stats: Arc<ConnectionCacheStats>,
533 ) -> Self {
534 Self {
535 client,
536 connection_stats,
537 }
538 }
539}
540
541#[async_trait]
542impl ClientConnection for QuicClientConnection {
543 fn server_addr(&self) -> &SocketAddr {
544 self.client.server_addr()
545 }
546
547 async fn send_data_batch(&self, buffers: &[Vec<u8>]) -> TransportResult<()> {
548 let stats = ClientStats::default();
549 let len = buffers.len();
550 let res = self
551 .client
552 .send_batch(buffers, &stats, self.connection_stats.clone())
553 .await;
554 self.connection_stats
555 .add_client_stats(&stats, len, res.is_ok());
556 res?;
557 Ok(())
558 }
559
560 async fn send_data(&self, data: &[u8]) -> TransportResult<()> {
561 let stats = Arc::new(ClientStats::default());
562 let num_packets = if data.is_empty() { 0 } else { 1 };
564 self.client
565 .send_buffer(data, &stats, self.connection_stats.clone())
566 .map_ok(|v| {
567 self.connection_stats
568 .add_client_stats(&stats, num_packets, true);
569 v
570 })
571 .map_err(|e| {
572 warn!(
573 "Failed to send data async to {}, error: {:?} ",
574 self.server_addr(),
575 e
576 );
577 datapoint_warn!("send-wire-async", ("failure", 1, i64),);
578 self.connection_stats
579 .add_client_stats(&stats, num_packets, false);
580 e.into()
581 })
582 .await
583 }
584}