1use std::{pin::Pin, sync::Arc};
5
6use async_trait::async_trait;
7use futures::{channel::mpsc, lock::Mutex, Stream, StreamExt as _};
8use linera_base::{data_types::Blob, identifiers::ChainId, time::Duration};
9use linera_core::{
10 data_types::CrossChainRequest,
11 node::NodeError,
12 worker::{NetworkActions, Notification, WorkerError, WorkerState},
13 JoinSetExt as _, ProcessConfirmedBlockMode,
14};
15use linera_storage::Storage;
16use tokio::{sync, sync::oneshot, task::JoinSet};
17use tokio_stream::wrappers::BroadcastStream;
18use tokio_util::sync::CancellationToken;
19use tracing::{debug, error, info, instrument};
20
21use super::transport::{MessageHandler, ServerHandle, TransportProtocol};
22use crate::{
23 config::{CrossChainConfig, ShardId, ValidatorInternalNetworkPreConfig},
24 cross_chain_message_queue, RpcMessage,
25};
26
27#[derive(Clone)]
29pub struct Server<S>
30where
31 S: Storage,
32{
33 network: ValidatorInternalNetworkPreConfig<TransportProtocol>,
34 host: String,
35 port: u16,
36 state: WorkerState<S>,
37 shard_id: ShardId,
38 cross_chain_config: CrossChainConfig,
39 packets_processed: u64,
41 user_errors: u64,
42}
43
44impl<S> Server<S>
45where
46 S: Storage,
47{
48 pub fn new(
50 network: ValidatorInternalNetworkPreConfig<TransportProtocol>,
51 host: String,
52 port: u16,
53 state: WorkerState<S>,
54 shard_id: ShardId,
55 cross_chain_config: CrossChainConfig,
56 ) -> Self {
57 Self {
58 network,
59 host,
60 port,
61 state,
62 shard_id,
63 cross_chain_config,
64 packets_processed: 0,
65 user_errors: 0,
66 }
67 }
68
69 pub fn packets_processed(&self) -> u64 {
71 self.packets_processed
72 }
73
74 pub fn user_errors(&self) -> u64 {
76 self.user_errors
77 }
78}
79
80impl<S> Server<S>
81where
82 S: Storage + Clone + Send + Sync + 'static,
83{
84 #[expect(clippy::too_many_arguments)]
85 async fn forward_cross_chain_queries(
86 nickname: String,
87 network: ValidatorInternalNetworkPreConfig<TransportProtocol>,
88 cross_chain_max_retries: u32,
89 cross_chain_retry_delay: Duration,
90 cross_chain_max_backoff: Duration,
91 cross_chain_sender_delay: Duration,
92 cross_chain_sender_failure_rate: f32,
93 this_shard: ShardId,
94 receiver: mpsc::Receiver<(CrossChainRequest, ShardId)>,
95 ) {
96 let pool = Arc::new(Mutex::new(
97 network
98 .protocol
99 .make_outgoing_connection_pool()
100 .await
101 .expect("Initialization should not fail"),
102 ));
103 let handle_request = move |shard_id, request| {
104 let pool = pool.clone();
105 let shard = network.shard(shard_id);
106 let remote_address = format!("{}:{}", shard.host, shard.port);
107 let message = RpcMessage::CrossChainRequest(Box::new(request));
108 async move {
109 pool.lock()
110 .await
111 .send_message_to(message.clone(), &remote_address)
112 .await?;
113 anyhow::Result::<_, anyhow::Error>::Ok(())
114 }
115 };
116 cross_chain_message_queue::forward_cross_chain_queries(
117 nickname,
118 cross_chain_max_retries,
119 cross_chain_retry_delay,
120 cross_chain_max_backoff,
121 cross_chain_sender_delay,
122 cross_chain_sender_failure_rate,
123 this_shard,
124 receiver,
125 handle_request,
126 )
127 .await;
128 }
129
130 pub fn spawn(
132 mut self,
133 shutdown_signal: CancellationToken,
134 join_set: &mut JoinSet<()>,
135 ) -> ServerHandle {
136 info!(
137 "Listening to {:?} traffic on {}:{}",
138 self.network.protocol, self.host, self.port
139 );
140 let address = (self.host.clone(), self.port);
141
142 let (cross_chain_sender, cross_chain_receiver) =
143 mpsc::channel(self.cross_chain_config.queue_size);
144
145 let (notification_sender, _) = sync::broadcast::channel(1000);
146
147 {
151 let routing_network = self.network.clone();
152 let routing_sender = cross_chain_sender.clone();
153 self.state = self
154 .state
155 .clone()
156 .with_outbound_cross_chain_sender(Arc::new(move |request| {
157 let shard_id = routing_network.get_shard_id(request.target_chain_id());
158 if let Err(error) = routing_sender.clone().try_send((request, shard_id)) {
159 tracing::error!(%error, "dropping cross-chain request");
160 }
161 }));
162 }
163
164 join_set.spawn_task(Self::forward_cross_chain_queries(
165 self.state.nickname().to_string(),
166 self.network.clone(),
167 self.cross_chain_config.max_retries,
168 Duration::from_millis(self.cross_chain_config.retry_delay_ms),
169 Duration::from_millis(self.cross_chain_config.max_backoff_ms),
170 Duration::from_millis(self.cross_chain_config.sender_delay_ms),
171 self.cross_chain_config.sender_failure_rate,
172 self.shard_id,
173 cross_chain_receiver,
174 ));
175
176 let protocol = self.network.protocol;
177 let state = RunningServerState {
178 server: self,
179 cross_chain_sender,
180 notification_sender,
181 };
182 protocol.spawn_server(address, state, shutdown_signal, join_set)
184 }
185}
186
187#[derive(Clone)]
188struct RunningServerState<S>
189where
190 S: Storage,
191{
192 server: Server<S>,
193 cross_chain_sender: mpsc::Sender<(CrossChainRequest, ShardId)>,
194 notification_sender: sync::broadcast::Sender<Notification>,
195}
196
197#[async_trait]
198impl<S> MessageHandler for RunningServerState<S>
199where
200 S: Storage + Clone + Send + Sync + 'static,
201{
202 #[instrument(
203 target = "simple_server",
204 skip_all,
205 fields(
206 nickname = self.server.state.nickname(),
207 chain_id = ?message.target_chain_id()
208 )
209 )]
210 async fn handle_message(&mut self, message: RpcMessage) -> Option<RpcMessage> {
211 let reply = match message {
212 RpcMessage::BlockProposal(message) => {
213 let (result, actions) = self.server.state.handle_block_proposal(*message).await;
214 self.handle_network_actions(actions);
220 match result {
221 Ok(info) => Ok(Some(RpcMessage::ChainInfoResponse(Box::new(info)))),
222 Err(error) => {
223 self.log_error(&error, "Failed to handle block proposal");
224 Err(error.into())
225 }
226 }
227 }
228 RpcMessage::LiteCertificate(request) => {
229 let (sender, receiver) = request
230 .wait_for_outgoing_messages
231 .then(oneshot::channel)
232 .unzip();
233 match Box::pin(
234 self.server
235 .state
236 .handle_lite_certificate(request.certificate, sender),
237 )
238 .await
239 {
240 Ok((info, actions)) => {
241 self.handle_network_actions(actions);
243 if let Some(receiver) = receiver {
244 if let Err(e) = receiver.await {
245 error!("Failed to wait for message delivery: {e}");
246 }
247 }
248 Ok(Some(RpcMessage::ChainInfoResponse(Box::new(info))))
250 }
251 Err(error) => {
252 let nickname = self.server.state.nickname();
253 if let WorkerError::MissingCertificateValue = &error {
254 debug!(nickname, %error, "Failed to handle lite certificate");
255 } else {
256 error!(nickname, %error, "Failed to handle lite certificate");
257 }
258 Err(error.into())
259 }
260 }
261 }
262 RpcMessage::TimeoutCertificate(request) => {
263 match self
264 .server
265 .state
266 .handle_timeout_certificate(request.certificate)
267 .await
268 {
269 Ok((info, actions)) => {
270 self.handle_network_actions(actions);
272 Ok(Some(RpcMessage::ChainInfoResponse(Box::new(info))))
274 }
275 Err(error) => {
276 self.log_error(&error, "Failed to handle timeout certificate");
277 Err(error.into())
278 }
279 }
280 }
281 RpcMessage::ValidatedCertificate(request) => {
282 match self
283 .server
284 .state
285 .handle_validated_certificate(request.certificate)
286 .await
287 {
288 Ok((info, actions)) => {
289 self.handle_network_actions(actions);
291 Ok(Some(RpcMessage::ChainInfoResponse(Box::new(info))))
293 }
294 Err(error) => {
295 self.log_error(&error, "Failed to handle validated certificate");
296 Err(error.into())
297 }
298 }
299 }
300 RpcMessage::ConfirmedCertificate(request) => {
301 let (sender, receiver) = request
302 .wait_for_outgoing_messages
303 .then(oneshot::channel)
304 .unzip();
305 match self
306 .server
307 .state
308 .handle_confirmed_certificate(
309 request.certificate,
310 ProcessConfirmedBlockMode::Auto,
311 sender,
312 )
313 .await
314 {
315 Ok((info, actions)) => {
316 self.handle_network_actions(actions);
318 if let Some(receiver) = receiver {
319 if let Err(e) = receiver.await {
320 error!("Failed to wait for message delivery: {e}");
321 }
322 }
323 Ok(Some(RpcMessage::ChainInfoResponse(Box::new(info))))
325 }
326 Err(error) => {
327 self.log_error(&error, "Failed to handle confirmed certificate");
328 Err(error.into())
329 }
330 }
331 }
332 RpcMessage::ChainInfoQuery(message) => {
333 match self.server.state.handle_chain_info_query(*message).await {
334 Ok(info) => Ok(Some(RpcMessage::ChainInfoResponse(Box::new(info)))),
335 Err(error) => {
336 self.log_error(&error, "Failed to handle chain info query");
337 Err(error.into())
338 }
339 }
340 }
341 RpcMessage::CrossChainRequest(request) => {
342 match self.server.state.handle_cross_chain_request(*request).await {
343 Ok(actions) => {
344 self.handle_network_actions(actions);
345 }
346 Err(error) => {
347 self.log_error(&error, "Failed to handle cross-chain request");
348 }
349 }
350 Ok(None)
352 }
353 RpcMessage::DownloadPendingBlob(request) => {
354 let (chain_id, blob_id) = *request;
355 match self
356 .server
357 .state
358 .download_pending_blob(chain_id, blob_id)
359 .await
360 {
361 Ok(blob) => Ok(Some(RpcMessage::DownloadPendingBlobResponse(Box::new(
362 blob.content().clone(),
363 )))),
364 Err(error) => {
365 self.log_error(&error, "Failed to handle pending blob request");
366 Err(error.into())
367 }
368 }
369 }
370 RpcMessage::HandlePendingBlob(request) => {
371 let (chain_id, blob_content) = *request;
372 match self
373 .server
374 .state
375 .handle_pending_blob(chain_id, Blob::new(blob_content))
376 .await
377 {
378 Ok(info) => Ok(Some(RpcMessage::ChainInfoResponse(Box::new(info)))),
379 Err(error) => {
380 self.log_error(&error, "Failed to handle pending blob");
381 Err(error.into())
382 }
383 }
384 }
385
386 RpcMessage::VersionInfoQuery => {
387 Ok(Some(RpcMessage::VersionInfoResponse(Box::default())))
388 }
389
390 RpcMessage::SubscribeNotifications(_) | RpcMessage::Notification(_) => {
391 Err(NodeError::UnexpectedMessage)
393 }
394
395 RpcMessage::Vote(_)
396 | RpcMessage::Error(_)
397 | RpcMessage::ChainInfoResponse(_)
398 | RpcMessage::VersionInfoResponse(_)
399 | RpcMessage::NetworkDescriptionQuery
400 | RpcMessage::NetworkDescriptionResponse(_)
401 | RpcMessage::ShardInfoQuery(_)
402 | RpcMessage::ShardInfoResponse(_)
403 | RpcMessage::DownloadBlob(_)
404 | RpcMessage::DownloadBlobs(_)
405 | RpcMessage::DownloadBlobResponse(_)
406 | RpcMessage::DownloadPendingBlobResponse(_)
407 | RpcMessage::DownloadConfirmedBlock(_)
408 | RpcMessage::DownloadConfirmedBlockResponse(_)
409 | RpcMessage::BlobLastUsedBy(_)
410 | RpcMessage::BlobLastUsedByResponse(_)
411 | RpcMessage::BlobLastUsedByCertificate(_)
412 | RpcMessage::BlobLastUsedByCertificateResponse(_)
413 | RpcMessage::MissingBlobIds(_)
414 | RpcMessage::MissingBlobIdsResponse(_)
415 | RpcMessage::EventBlockHeights(_)
416 | RpcMessage::EventBlockHeightsResponse(_)
417 | RpcMessage::DownloadCertificates(_)
418 | RpcMessage::DownloadCertificatesResponse(_)
419 | RpcMessage::UploadBlob(_)
420 | RpcMessage::UploadBlobResponse(_)
421 | RpcMessage::DownloadCertificatesByHeights(_, _)
422 | RpcMessage::DownloadCertificatesByHeightsResponse(_) => {
423 Err(NodeError::UnexpectedMessage)
424 }
425 };
426
427 self.server.packets_processed += 1;
428 #[allow(unknown_lints)]
430 #[expect(clippy::manual_is_multiple_of)]
431 if self.server.packets_processed % 5000 == 0 {
432 debug!(
433 "[{}] {}:{} (shard {}) has processed {} packets",
434 self.server.state.nickname(),
435 self.server.host,
436 self.server.port,
437 self.server.shard_id,
438 self.server.packets_processed
439 );
440 }
441
442 match reply {
443 Ok(x) => x,
444 Err(error) => {
445 debug!(
447 "[{}] User query failed: {}",
448 self.server.state.nickname(),
449 error
450 );
451 self.server.user_errors += 1;
452 Some(error.into())
453 }
454 }
455 }
456
457 async fn handle_subscribe(
458 &mut self,
459 chains: Vec<ChainId>,
460 ) -> Option<Pin<Box<dyn Stream<Item = RpcMessage> + Send>>> {
461 RunningServerState::subscribe_to_notifications(self, chains).await
462 }
463}
464
465impl<S> RunningServerState<S>
466where
467 S: Storage + Clone + Send + Sync + 'static,
468{
469 async fn subscribe_to_notifications(
470 &self,
471 chains: Vec<ChainId>,
472 ) -> Option<Pin<Box<dyn Stream<Item = RpcMessage> + Send>>> {
473 let receiver = self.notification_sender.subscribe();
474 let stream = BroadcastStream::new(receiver).filter_map(move |result| {
475 let chains = chains.clone();
476 async move {
477 match result {
478 Ok(notification) if chains.contains(¬ification.chain_id) => {
479 Some(RpcMessage::Notification(Box::new(notification)))
480 }
481 _ => None,
482 }
483 }
484 });
485 Some(Box::pin(stream))
486 }
487}
488
489impl<S> RunningServerState<S>
490where
491 S: Storage + Send,
492{
493 fn handle_network_actions(&mut self, actions: NetworkActions) {
494 for request in actions.cross_chain_requests {
495 let shard_id = self.server.network.get_shard_id(request.target_chain_id());
496 debug!(
497 "[{}] Scheduling cross-chain query: {} -> {}",
498 self.server.state.nickname(),
499 self.server.shard_id,
500 shard_id
501 );
502 if let Err(error) = self.cross_chain_sender.try_send((request, shard_id)) {
503 error!(%error, "dropping cross-chain request");
504 break;
505 }
506 }
507 for notification in actions.notifications {
508 debug!("Scheduling notification query");
509 if let Err(error) = self.notification_sender.send(notification) {
510 debug!(%error, "dropping notification (no receivers)");
511 }
512 }
513 }
514
515 fn log_error(&self, error: &WorkerError, context: &str) {
516 let nickname = self.server.state.nickname();
517 if error.is_local() {
518 error!(nickname, %error, "{}", context);
519 } else {
520 debug!(nickname, %error, "{}", context);
521 }
522 }
523}