1use std::sync::Arc;
6
7use futures::FutureExt;
8use tokio::{
9 sync::mpsc,
10 task::JoinSet,
11 time::{sleep, Duration},
12};
13use tower::{buffer::Buffer, util::BoxCloneService, Service, ServiceExt};
14use tracing::{instrument, Instrument, Span};
15
16use cuprate_async_buffer::BufferStream;
17use cuprate_p2p_core::{
18 client::Connector,
19 client::PeerSyncCallback,
20 services::{AddressBookRequest, AddressBookResponse},
21 CoreSyncSvc, NetworkZone, ProtocolRequestHandlerMaker, Transport,
22};
23
24pub mod block_downloader;
25mod broadcast;
26pub mod config;
27pub mod connection_maintainer;
28pub mod constants;
29mod inbound_server;
30mod peer_set;
31
32use block_downloader::{BlockBatch, BlockDownloaderConfig, ChainSvcRequest, ChainSvcResponse};
33pub use broadcast::{BroadcastRequest, BroadcastSvc};
34pub use config::{AddressBookConfig, P2PConfig, TransportConfig};
35use connection_maintainer::MakeConnectionRequest;
36use peer_set::PeerSet;
37pub use peer_set::{ClientDropGuard, PeerSetRequest, PeerSetResponse};
38
39const INBOUND_CONNECTION_MONITOR_INTERVAL: Duration = Duration::from_secs(3600);
41
42#[expect(clippy::infinite_loop)]
47async fn inbound_connection_monitor(
48 inbound_semaphore: Arc<tokio::sync::Semaphore>,
49 max_inbound_connections: usize,
50 p2p_port: u16,
51) {
52 if max_inbound_connections == 0 {
54 return;
55 }
56
57 loop {
58 sleep(INBOUND_CONNECTION_MONITOR_INTERVAL).await;
60
61 let available_permits = inbound_semaphore.available_permits();
64 if available_permits == max_inbound_connections {
65 tracing::warn!(
66 "No incoming connections - check firewalls/routers allow port {}",
67 p2p_port
68 );
69 }
70 }
71}
72
73#[instrument(level = "error", name = "net", skip_all, fields(zone = Z::NAME))]
84pub async fn initialize_network<Z, T, PR, CS>(
85 protocol_request_handler_maker: PR,
86 core_sync_svc: CS,
87 config: P2PConfig<Z>,
88 transport_config: TransportConfig<Z, T>,
89 peer_sync_callback: Option<PeerSyncCallback>,
90) -> Result<NetworkInterface<Z>, tower::BoxError>
91where
92 Z: NetworkZone,
93 T: Transport<Z>,
94 Z::Addr: borsh::BorshDeserialize + borsh::BorshSerialize,
95 PR: ProtocolRequestHandlerMaker<Z> + Clone,
96 CS: CoreSyncSvc + Clone,
97{
98 let max_connections = config
99 .max_inbound_connections
100 .checked_add(config.outbound_connections)
101 .unwrap()
102 .max(1);
103
104 let address_book = Buffer::new(
105 cuprate_address_book::init_address_book(config.address_book_config.clone()).await?,
106 max_connections,
107 );
108
109 let (broadcast_svc, outbound_mkr, inbound_mkr) =
112 broadcast::init_broadcast_channels(broadcast::BroadcastConfig::default());
113
114 let (new_connection_tx, new_connection_rx) = mpsc::channel(max_connections);
115 let (make_connection_tx, make_connection_rx) = mpsc::channel(3);
116
117 let peer_set = PeerSet::new(new_connection_rx);
118
119 if config.offline {
120 tracing::warn!("Offline mode enabled, not connecting to or listening for peers.");
121
122 return Ok(NetworkInterface {
123 peer_set: Buffer::new(peer_set, 10).boxed_clone(),
124 broadcast_svc,
125 make_connection_tx,
126 address_book: address_book.boxed_clone(),
127 _background_tasks: Arc::new(JoinSet::new()),
128 });
129 }
130
131 let mut basic_node_data = config.basic_node_data();
132
133 if !Z::CHECK_NODE_ID {
134 basic_node_data.peer_id = 1;
135 }
136
137 let mut outbound_handshaker_builder =
138 cuprate_p2p_core::client::HandshakerBuilder::<Z, T, _, _, _, _>::new(
139 basic_node_data,
140 transport_config.client_config,
141 )
142 .with_address_book(address_book.clone())
143 .with_core_sync_svc(core_sync_svc)
144 .with_protocol_request_handler_maker(protocol_request_handler_maker)
145 .with_broadcast_stream_maker(outbound_mkr)
146 .with_connection_parent_span(Span::current());
147
148 if let Some(ref cb) = peer_sync_callback {
149 outbound_handshaker_builder =
150 outbound_handshaker_builder.with_peer_sync_callback(cb.clone());
151 }
152
153 let inbound_handshaker = outbound_handshaker_builder
154 .clone()
155 .with_broadcast_stream_maker(inbound_mkr)
156 .build();
157
158 let outbound_handshaker = outbound_handshaker_builder.build();
159
160 let outbound_connector = Connector::new(outbound_handshaker);
161 let outbound_connection_maintainer = connection_maintainer::OutboundConnectionKeeper::new(
162 config.clone(),
163 new_connection_tx.clone(),
164 make_connection_rx,
165 address_book.clone(),
166 outbound_connector,
167 peer_sync_callback.clone(),
168 );
169
170 let inbound_semaphore = Arc::new(tokio::sync::Semaphore::new(config.max_inbound_connections));
172
173 let mut background_tasks = JoinSet::new();
174
175 background_tasks.spawn(
176 outbound_connection_maintainer
177 .run()
178 .instrument(Span::current()),
179 );
180
181 if transport_config.server_config.is_some() {
183 background_tasks.spawn(
184 inbound_connection_monitor(
185 Arc::clone(&inbound_semaphore),
186 config.max_inbound_connections,
187 config.p2p_port,
188 )
189 .instrument(tracing::info_span!("inbound_connection_monitor")),
190 );
191 }
192
193 background_tasks.spawn(
194 inbound_server::inbound_server(
195 new_connection_tx,
196 inbound_handshaker,
197 address_book.clone(),
198 config,
199 transport_config.server_config,
200 inbound_semaphore,
201 peer_sync_callback,
202 )
203 .map(|res| {
204 if let Err(e) = res {
205 tracing::error!("Error in inbound connection listener: {e}");
206 }
207
208 tracing::info!("Inbound connection listener shutdown");
209 })
210 .instrument(Span::current()),
211 );
212
213 Ok(NetworkInterface {
214 peer_set: Buffer::new(peer_set, 10).boxed_clone(),
215 broadcast_svc,
216 make_connection_tx,
217 address_book: address_book.boxed_clone(),
218 _background_tasks: Arc::new(background_tasks),
219 })
220}
221
222#[derive(Clone)]
224pub struct NetworkInterface<N: NetworkZone> {
225 peer_set: BoxCloneService<PeerSetRequest, PeerSetResponse<N>, tower::BoxError>,
227 broadcast_svc: BroadcastSvc<N>,
229 #[expect(dead_code, reason = "will be used eventually")]
231 make_connection_tx: mpsc::Sender<MakeConnectionRequest>,
232 address_book: BoxCloneService<AddressBookRequest<N>, AddressBookResponse<N>, tower::BoxError>,
234 _background_tasks: Arc<JoinSet<()>>,
236}
237
238impl<N: NetworkZone> NetworkInterface<N> {
239 pub fn broadcast_svc(&self) -> BroadcastSvc<N> {
241 self.broadcast_svc.clone()
242 }
243
244 pub fn block_downloader<C>(
246 &self,
247 our_chain_service: C,
248 config: BlockDownloaderConfig,
249 ) -> BufferStream<BlockBatch>
250 where
251 C: Service<ChainSvcRequest<N>, Response = ChainSvcResponse<N>, Error = tower::BoxError>
252 + Send
253 + 'static,
254 C::Future: Send + 'static,
255 {
256 block_downloader::download_blocks(self.peer_set.clone(), our_chain_service, config)
257 }
258
259 pub fn address_book(
261 &self,
262 ) -> BoxCloneService<AddressBookRequest<N>, AddressBookResponse<N>, tower::BoxError> {
263 self.address_book.clone()
264 }
265
266 pub fn peer_set(
268 &mut self,
269 ) -> &mut BoxCloneService<PeerSetRequest, PeerSetResponse<N>, tower::BoxError> {
270 &mut self.peer_set
271 }
272}