snarkos_node_tcp/protocols/on_connect.rs
1// Copyright (c) 2019-2026 Provable Inc.
2// This file is part of the snarkOS library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use std::net::SocketAddr;
17
18use tokio::sync::{mpsc, oneshot};
19use tracing::*;
20
21#[cfg(doc)]
22use crate::{
23 Connection,
24 protocols::{Reading, Writing},
25};
26use crate::{
27 P2P,
28 connections::DisconnectOrigin,
29 protocols::{DisconnectOnDrop, ProtocolHandler},
30};
31
32/// Can be used to automatically perform some initial actions once the connection with a peer is
33/// fully established.
34#[async_trait::async_trait]
35pub trait OnConnect: P2P
36where
37 Self: Clone + Send + Sync + 'static,
38{
39 /// Attaches the behavior specified in [`OnConnect::on_connect`] right after every successful
40 /// handshake.
41 async fn enable_on_connect(&self) {
42 let (from_node_sender, mut from_node_receiver) = mpsc::channel::<(
43 SocketAddr,
44 oneshot::Sender<tokio::task::JoinHandle<()>>,
45 )>(self.tcp().config().max_connections as usize);
46
47 // use a channel to know when the on_connect task is ready
48 let (tx, rx) = oneshot::channel::<()>();
49
50 // spawn a background task dedicated to executing the desired post-handshake actions
51 let self_clone = self.clone();
52 let on_connect_task = tokio::spawn(async move {
53 trace!(parent: self_clone.tcp().span(), "spawned the OnConnect handler task");
54 if tx.send(()).is_err() {
55 error!(parent: self_clone.tcp().span(), "OnConnect handler creation interrupted! shutting down the node");
56 self_clone.tcp().shut_down().await;
57 return;
58 }
59
60 while let Some((addr, notifier)) = from_node_receiver.recv().await {
61 let self_clone2 = self_clone.clone();
62 let handle = tokio::spawn(async move {
63 // disconnect automatically if the OnConnect impl panics
64 let mut conn_cleanup =
65 DisconnectOnDrop::new(self_clone2.tcp().clone(), addr, DisconnectOrigin::OnConnectAbort);
66 // perform the specified initial actions
67 self_clone2.on_connect(addr).await;
68 // if there was no panic, do not disconnect - this "defuses" the auto-cleanup
69 conn_cleanup.node.take();
70 });
71 // notify the node that the initial actions have concluded
72 let _ = notifier.send(handle); // can't really fail
73 }
74 });
75 let _ = rx.await;
76 self.tcp().tasks.lock().push(on_connect_task);
77
78 // register the OnConnect handler with the Node
79 let hdl = Box::new(ProtocolHandler(from_node_sender));
80 assert!(self.tcp().protocols.on_connect.set(hdl).is_ok(), "the OnConnect protocol was enabled more than once!");
81 }
82
83 /// Any initial actions to be executed after the handshake is concluded; in order to be able to
84 /// communicate with the peer in the usual manner (i.e. via [`Writing`]), only its [`SocketAddr`]
85 /// (as opposed to the related [`Connection`] object) is provided as an argument.
86 async fn on_connect(&self, addr: SocketAddr);
87}