solana_udp_client/
lib.rs

1#![allow(clippy::arithmetic_side_effects)]
2
3pub mod nonblocking;
4pub mod udp_client;
5
6use {
7    crate::{
8        nonblocking::udp_client::UdpClientConnection as NonblockingUdpConnection,
9        udp_client::UdpClientConnection as BlockingUdpConnection,
10    },
11    solana_connection_cache::{
12        connection_cache::{
13            BaseClientConnection, ClientError, ConnectionManager, ConnectionPool,
14            ConnectionPoolError, NewConnectionConfig, Protocol,
15        },
16        connection_cache_stats::ConnectionCacheStats,
17    },
18    solana_keypair::Keypair,
19    solana_net_utils::sockets::{self, SocketConfiguration},
20    std::{
21        net::{SocketAddr, UdpSocket},
22        sync::Arc,
23    },
24};
25
26pub struct UdpPool {
27    connections: Vec<Arc<Udp>>,
28}
29impl ConnectionPool for UdpPool {
30    type BaseClientConnection = Udp;
31    type NewConnectionConfig = UdpConfig;
32
33    fn add_connection(&mut self, config: &Self::NewConnectionConfig, addr: &SocketAddr) -> usize {
34        let connection = self.create_pool_entry(config, addr);
35        let idx = self.connections.len();
36        self.connections.push(connection);
37        idx
38    }
39
40    fn num_connections(&self) -> usize {
41        self.connections.len()
42    }
43
44    fn get(&self, index: usize) -> Result<Arc<Self::BaseClientConnection>, ConnectionPoolError> {
45        self.connections
46            .get(index)
47            .cloned()
48            .ok_or(ConnectionPoolError::IndexOutOfRange)
49    }
50
51    fn create_pool_entry(
52        &self,
53        config: &Self::NewConnectionConfig,
54        _addr: &SocketAddr,
55    ) -> Arc<Self::BaseClientConnection> {
56        Arc::new(Udp(config.udp_socket.clone()))
57    }
58}
59
60pub struct UdpConfig {
61    udp_socket: Arc<UdpSocket>,
62}
63
64impl NewConnectionConfig for UdpConfig {
65    fn new() -> Result<Self, ClientError> {
66        // Use UNSPECIFIED for production validators to bind to all interfaces
67        // Use LOCALHOST only in dev/test context to avoid port conflicts in CI
68        #[cfg(not(feature = "dev-context-only-utils"))]
69        let bind_ip = std::net::IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED);
70        #[cfg(feature = "dev-context-only-utils")]
71        let bind_ip = std::net::IpAddr::V4(std::net::Ipv4Addr::LOCALHOST);
72
73        // This will bind to random ports, but VALIDATOR_PORT_RANGE is outside
74        // of the range for CI tests when this is running in CI
75        let socket = sockets::bind_in_range_with_config(
76            bind_ip,
77            solana_net_utils::VALIDATOR_PORT_RANGE,
78            SocketConfiguration::default(),
79        )
80        .map_err(Into::<ClientError>::into)?;
81        Ok(Self {
82            udp_socket: Arc::new(socket.1),
83        })
84    }
85}
86
87pub struct Udp(Arc<UdpSocket>);
88impl BaseClientConnection for Udp {
89    type BlockingClientConnection = BlockingUdpConnection;
90    type NonblockingClientConnection = NonblockingUdpConnection;
91
92    fn new_blocking_connection(
93        &self,
94        addr: SocketAddr,
95        _stats: Arc<ConnectionCacheStats>,
96    ) -> Arc<Self::BlockingClientConnection> {
97        Arc::new(BlockingUdpConnection::new_from_addr(self.0.clone(), addr))
98    }
99
100    fn new_nonblocking_connection(
101        &self,
102        addr: SocketAddr,
103        _stats: Arc<ConnectionCacheStats>,
104    ) -> Arc<Self::NonblockingClientConnection> {
105        Arc::new(NonblockingUdpConnection::new_from_addr(
106            self.0.try_clone().unwrap(),
107            addr,
108        ))
109    }
110}
111
112#[derive(Default)]
113pub struct UdpConnectionManager {}
114
115impl ConnectionManager for UdpConnectionManager {
116    type ConnectionPool = UdpPool;
117    type NewConnectionConfig = UdpConfig;
118
119    const PROTOCOL: Protocol = Protocol::UDP;
120
121    fn new_connection_pool(&self) -> Self::ConnectionPool {
122        UdpPool {
123            connections: Vec::default(),
124        }
125    }
126
127    fn new_connection_config(&self) -> Self::NewConnectionConfig {
128        UdpConfig::new().unwrap()
129    }
130
131    fn update_key(&self, _key: &Keypair) -> Result<(), Box<dyn std::error::Error>> {
132        Ok(())
133    }
134}