Skip to main content

cuprate_p2p_core/client/handshaker/
builder.rs

1use std::{convert::Infallible, marker::PhantomData};
2
3use futures::{stream, Stream};
4use tower::{make::Shared, util::MapErr};
5use tracing::Span;
6
7use cuprate_wire::BasicNodeData;
8
9use crate::{
10    client::{handshaker::HandShaker, InternalPeerID, PeerSyncCallback},
11    AddressBook, BroadcastMessage, CoreSyncSvc, NetworkZone, ProtocolRequestHandlerMaker,
12    Transport,
13};
14
15mod dummy;
16pub use dummy::{DummyAddressBook, DummyCoreSyncSvc, DummyProtocolRequestHandler};
17
18/// A [`HandShaker`] [`Service`](tower::Service) builder.
19///
20/// This builder applies default values to make usage easier, behaviour and drawbacks of the defaults are documented
21/// on the `with_*` method to change it, for example [`HandshakerBuilder::with_protocol_request_handler_maker`].
22///
23/// If you want to use any network other than [`Mainnet`](crate::Network::Mainnet)
24/// you will need to change the core sync service with [`HandshakerBuilder::with_core_sync_svc`],
25/// see that method for details.
26#[derive(Debug, Clone)]
27pub struct HandshakerBuilder<
28    N: NetworkZone,
29    T: Transport<N>,
30    AdrBook = DummyAddressBook,
31    CSync = DummyCoreSyncSvc,
32    ProtoHdlrMkr = MapErr<Shared<DummyProtocolRequestHandler>, fn(Infallible) -> tower::BoxError>,
33    BrdcstStrmMkr = fn(
34        InternalPeerID<<N as NetworkZone>::Addr>,
35    ) -> stream::Pending<BroadcastMessage>,
36> {
37    /// The address book service.
38    address_book: AdrBook,
39    /// The core sync data service.
40    core_sync_svc: CSync,
41    /// The protocol request service.
42    protocol_request_svc_maker: ProtoHdlrMkr,
43    /// Our [`BasicNodeData`]
44    our_basic_node_data: BasicNodeData,
45    /// A function that returns a stream that will give items to be broadcast by a connection.
46    broadcast_stream_maker: BrdcstStrmMkr,
47    /// The [`Span`] that will set as the parent to the connection [`Span`].
48    connection_parent_span: Option<Span>,
49    /// Called with a peer's [`CoreSyncData`].
50    on_peer_sync: Option<PeerSyncCallback>,
51
52    /// Transport method client configuration to use.
53    transport_client_config: T::ClientConfig,
54    /// The network zone.
55    _zone: PhantomData<N>,
56}
57
58impl<N: NetworkZone, T: Transport<N>> HandshakerBuilder<N, T> {
59    /// Creates a new builder with our node's basic node data.
60    pub fn new(
61        our_basic_node_data: BasicNodeData,
62        transport_client_config: T::ClientConfig,
63    ) -> Self {
64        Self {
65            address_book: DummyAddressBook,
66            core_sync_svc: DummyCoreSyncSvc::static_mainnet_genesis(),
67            protocol_request_svc_maker: MapErr::new(
68                Shared::new(DummyProtocolRequestHandler),
69                tower::BoxError::from,
70            ),
71            our_basic_node_data,
72            broadcast_stream_maker: |_| stream::pending(),
73            connection_parent_span: None,
74            on_peer_sync: None,
75            transport_client_config,
76            _zone: PhantomData,
77        }
78    }
79}
80
81impl<N: NetworkZone, T: Transport<N>, AdrBook, CSync, ProtoHdlr, BrdcstStrmMkr>
82    HandshakerBuilder<N, T, AdrBook, CSync, ProtoHdlr, BrdcstStrmMkr>
83{
84    /// Changes the address book to the provided one.
85    ///
86    /// ## Default Address Book
87    ///
88    /// The default address book is used if this function is not called.
89    ///
90    /// The default address book's only drawback is that it does not keep track of peers and therefore
91    /// bans.
92    pub fn with_address_book<NAdrBook>(
93        self,
94        new_address_book: NAdrBook,
95    ) -> HandshakerBuilder<N, T, NAdrBook, CSync, ProtoHdlr, BrdcstStrmMkr>
96    where
97        NAdrBook: AddressBook<N> + Clone,
98    {
99        let Self {
100            core_sync_svc,
101            protocol_request_svc_maker,
102            our_basic_node_data,
103            broadcast_stream_maker,
104            connection_parent_span,
105            on_peer_sync,
106            transport_client_config,
107            ..
108        } = self;
109
110        HandshakerBuilder {
111            address_book: new_address_book,
112            core_sync_svc,
113            protocol_request_svc_maker,
114            our_basic_node_data,
115            broadcast_stream_maker,
116            connection_parent_span,
117            on_peer_sync,
118            transport_client_config,
119            _zone: PhantomData,
120        }
121    }
122
123    /// Changes the core sync service to the provided one.
124    ///
125    /// The core sync service should keep track of our nodes core sync data.
126    ///
127    /// ## Default Core Sync Service
128    ///
129    /// The default core sync service is used if this method is not called.
130    ///
131    /// The default core sync service will just use the mainnet genesis block, to use other network's
132    /// genesis see [`DummyCoreSyncSvc::static_stagenet_genesis`] and [`DummyCoreSyncSvc::static_testnet_genesis`].
133    /// The drawbacks to keeping this the default is that it will always return the mainnet genesis as our nodes
134    /// sync info, which means peers won't know our actual chain height, this may or may not be a problem for
135    /// different use cases.
136    pub fn with_core_sync_svc<NCSync>(
137        self,
138        new_core_sync_svc: NCSync,
139    ) -> HandshakerBuilder<N, T, AdrBook, NCSync, ProtoHdlr, BrdcstStrmMkr>
140    where
141        NCSync: CoreSyncSvc + Clone,
142    {
143        let Self {
144            address_book,
145            protocol_request_svc_maker,
146            our_basic_node_data,
147            broadcast_stream_maker,
148            connection_parent_span,
149            on_peer_sync,
150            transport_client_config,
151            ..
152        } = self;
153
154        HandshakerBuilder {
155            address_book,
156            core_sync_svc: new_core_sync_svc,
157            protocol_request_svc_maker,
158            our_basic_node_data,
159            broadcast_stream_maker,
160            connection_parent_span,
161            on_peer_sync,
162            transport_client_config,
163            _zone: PhantomData,
164        }
165    }
166
167    /// Changes the protocol request handler maker, which creates the service that handles [`ProtocolRequest`](crate::ProtocolRequest)s
168    /// to our node.
169    ///
170    /// ## Default Protocol Request Handler
171    ///
172    /// The default service maker will create services that will not respond to any protocol requests, this should not
173    /// be an issue as long as peers do not think we are ahead of them, if they do they will send requests
174    /// for our blocks, and we won't respond which will cause them to disconnect.
175    pub fn with_protocol_request_handler_maker<NProtoHdlrMkr>(
176        self,
177        new_protocol_request_svc_maker: NProtoHdlrMkr,
178    ) -> HandshakerBuilder<N, T, AdrBook, CSync, NProtoHdlrMkr, BrdcstStrmMkr>
179    where
180        NProtoHdlrMkr: ProtocolRequestHandlerMaker<N> + Clone,
181    {
182        let Self {
183            address_book,
184            core_sync_svc,
185            our_basic_node_data,
186            broadcast_stream_maker,
187            connection_parent_span,
188            on_peer_sync,
189            transport_client_config,
190            ..
191        } = self;
192
193        HandshakerBuilder {
194            address_book,
195            core_sync_svc,
196            protocol_request_svc_maker: new_protocol_request_svc_maker,
197            our_basic_node_data,
198            broadcast_stream_maker,
199            connection_parent_span,
200            on_peer_sync,
201            transport_client_config,
202            _zone: PhantomData,
203        }
204    }
205
206    /// Changes the broadcast stream maker, which is used to create streams that yield messages to broadcast.
207    ///
208    /// ## Default Broadcast Stream Maker
209    ///
210    /// The default broadcast stream maker just returns [`stream::Pending`], i.e. the returned stream will not
211    /// produce any messages to broadcast, this is not a problem if your use case does not require broadcasting
212    /// messages.
213    pub fn with_broadcast_stream_maker<NBrdcstStrmMkr, BrdcstStrm>(
214        self,
215        new_broadcast_stream_maker: NBrdcstStrmMkr,
216    ) -> HandshakerBuilder<N, T, AdrBook, CSync, ProtoHdlr, NBrdcstStrmMkr>
217    where
218        BrdcstStrm: Stream<Item = BroadcastMessage> + Send + 'static,
219        NBrdcstStrmMkr: Fn(InternalPeerID<N::Addr>) -> BrdcstStrm + Clone + Send + 'static,
220    {
221        let Self {
222            address_book,
223            core_sync_svc,
224            protocol_request_svc_maker,
225            our_basic_node_data,
226            connection_parent_span,
227            on_peer_sync,
228            transport_client_config,
229            ..
230        } = self;
231
232        HandshakerBuilder {
233            address_book,
234            core_sync_svc,
235            protocol_request_svc_maker,
236            our_basic_node_data,
237            broadcast_stream_maker: new_broadcast_stream_maker,
238            connection_parent_span,
239            on_peer_sync,
240            transport_client_config,
241            _zone: PhantomData,
242        }
243    }
244
245    /// Sets the callback invoked with a peer's [`CoreSyncData`](cuprate_wire::CoreSyncData).
246    ///
247    /// ## Default
248    ///
249    /// No callback is set by default.
250    #[must_use]
251    pub fn with_peer_sync_callback(self, on_peer_sync: PeerSyncCallback) -> Self {
252        Self {
253            on_peer_sync: Some(on_peer_sync),
254            ..self
255        }
256    }
257
258    /// Changes the parent [`Span`] of the connection task to the one provided.
259    ///
260    /// ## Default Connection Parent Span
261    ///
262    /// The default connection span will be [`Span::none`].
263    #[must_use]
264    pub fn with_connection_parent_span(self, connection_parent_span: Span) -> Self {
265        Self {
266            connection_parent_span: Some(connection_parent_span),
267            ..self
268        }
269    }
270
271    /// Builds the [`HandShaker`].
272    pub fn build(self) -> HandShaker<N, T, AdrBook, CSync, ProtoHdlr, BrdcstStrmMkr> {
273        HandShaker::new(
274            self.address_book,
275            self.core_sync_svc,
276            self.protocol_request_svc_maker,
277            self.broadcast_stream_maker,
278            self.our_basic_node_data,
279            self.connection_parent_span.unwrap_or(Span::none()),
280            self.on_peer_sync,
281            self.transport_client_config,
282        )
283    }
284}