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},
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
50    /// Transport method client configuration to use.
51    transport_client_config: T::ClientConfig,
52    /// The network zone.
53    _zone: PhantomData<N>,
54}
55
56impl<N: NetworkZone, T: Transport<N>> HandshakerBuilder<N, T> {
57    /// Creates a new builder with our node's basic node data.
58    pub fn new(
59        our_basic_node_data: BasicNodeData,
60        transport_client_config: T::ClientConfig,
61    ) -> Self {
62        Self {
63            address_book: DummyAddressBook,
64            core_sync_svc: DummyCoreSyncSvc::static_mainnet_genesis(),
65            protocol_request_svc_maker: MapErr::new(
66                Shared::new(DummyProtocolRequestHandler),
67                tower::BoxError::from,
68            ),
69            our_basic_node_data,
70            broadcast_stream_maker: |_| stream::pending(),
71            connection_parent_span: None,
72            transport_client_config,
73            _zone: PhantomData,
74        }
75    }
76}
77
78impl<N: NetworkZone, T: Transport<N>, AdrBook, CSync, ProtoHdlr, BrdcstStrmMkr>
79    HandshakerBuilder<N, T, AdrBook, CSync, ProtoHdlr, BrdcstStrmMkr>
80{
81    /// Changes the address book to the provided one.
82    ///
83    /// ## Default Address Book
84    ///
85    /// The default address book is used if this function is not called.
86    ///
87    /// The default address book's only drawback is that it does not keep track of peers and therefore
88    /// bans.
89    pub fn with_address_book<NAdrBook>(
90        self,
91        new_address_book: NAdrBook,
92    ) -> HandshakerBuilder<N, T, NAdrBook, CSync, ProtoHdlr, BrdcstStrmMkr>
93    where
94        NAdrBook: AddressBook<N> + Clone,
95    {
96        let Self {
97            core_sync_svc,
98            protocol_request_svc_maker,
99            our_basic_node_data,
100            broadcast_stream_maker,
101            connection_parent_span,
102            transport_client_config,
103            ..
104        } = self;
105
106        HandshakerBuilder {
107            address_book: new_address_book,
108            core_sync_svc,
109            protocol_request_svc_maker,
110            our_basic_node_data,
111            broadcast_stream_maker,
112            connection_parent_span,
113            transport_client_config,
114            _zone: PhantomData,
115        }
116    }
117
118    /// Changes the core sync service to the provided one.
119    ///
120    /// The core sync service should keep track of our nodes core sync data.
121    ///
122    /// ## Default Core Sync Service
123    ///
124    /// The default core sync service is used if this method is not called.
125    ///
126    /// The default core sync service will just use the mainnet genesis block, to use other network's
127    /// genesis see [`DummyCoreSyncSvc::static_stagenet_genesis`] and [`DummyCoreSyncSvc::static_testnet_genesis`].
128    /// The drawbacks to keeping this the default is that it will always return the mainnet genesis as our nodes
129    /// sync info, which means peers won't know our actual chain height, this may or may not be a problem for
130    /// different use cases.
131    pub fn with_core_sync_svc<NCSync>(
132        self,
133        new_core_sync_svc: NCSync,
134    ) -> HandshakerBuilder<N, T, AdrBook, NCSync, ProtoHdlr, BrdcstStrmMkr>
135    where
136        NCSync: CoreSyncSvc + Clone,
137    {
138        let Self {
139            address_book,
140            protocol_request_svc_maker,
141            our_basic_node_data,
142            broadcast_stream_maker,
143            connection_parent_span,
144            transport_client_config,
145            ..
146        } = self;
147
148        HandshakerBuilder {
149            address_book,
150            core_sync_svc: new_core_sync_svc,
151            protocol_request_svc_maker,
152            our_basic_node_data,
153            broadcast_stream_maker,
154            connection_parent_span,
155            transport_client_config,
156            _zone: PhantomData,
157        }
158    }
159
160    /// Changes the protocol request handler maker, which creates the service that handles [`ProtocolRequest`](crate::ProtocolRequest)s
161    /// to our node.
162    ///
163    /// ## Default Protocol Request Handler
164    ///
165    /// The default service maker will create services that will not respond to any protocol requests, this should not
166    /// be an issue as long as peers do not think we are ahead of them, if they do they will send requests
167    /// for our blocks, and we won't respond which will cause them to disconnect.
168    pub fn with_protocol_request_handler_maker<NProtoHdlrMkr>(
169        self,
170        new_protocol_request_svc_maker: NProtoHdlrMkr,
171    ) -> HandshakerBuilder<N, T, AdrBook, CSync, NProtoHdlrMkr, BrdcstStrmMkr>
172    where
173        NProtoHdlrMkr: ProtocolRequestHandlerMaker<N> + Clone,
174    {
175        let Self {
176            address_book,
177            core_sync_svc,
178            our_basic_node_data,
179            broadcast_stream_maker,
180            connection_parent_span,
181            transport_client_config,
182            ..
183        } = self;
184
185        HandshakerBuilder {
186            address_book,
187            core_sync_svc,
188            protocol_request_svc_maker: new_protocol_request_svc_maker,
189            our_basic_node_data,
190            broadcast_stream_maker,
191            connection_parent_span,
192            transport_client_config,
193            _zone: PhantomData,
194        }
195    }
196
197    /// Changes the broadcast stream maker, which is used to create streams that yield messages to broadcast.
198    ///
199    /// ## Default Broadcast Stream Maker
200    ///
201    /// The default broadcast stream maker just returns [`stream::Pending`], i.e. the returned stream will not
202    /// produce any messages to broadcast, this is not a problem if your use case does not require broadcasting
203    /// messages.
204    pub fn with_broadcast_stream_maker<NBrdcstStrmMkr, BrdcstStrm>(
205        self,
206        new_broadcast_stream_maker: NBrdcstStrmMkr,
207    ) -> HandshakerBuilder<N, T, AdrBook, CSync, ProtoHdlr, NBrdcstStrmMkr>
208    where
209        BrdcstStrm: Stream<Item = BroadcastMessage> + Send + 'static,
210        NBrdcstStrmMkr: Fn(InternalPeerID<N::Addr>) -> BrdcstStrm + Clone + Send + 'static,
211    {
212        let Self {
213            address_book,
214            core_sync_svc,
215            protocol_request_svc_maker,
216            our_basic_node_data,
217            connection_parent_span,
218            transport_client_config,
219            ..
220        } = self;
221
222        HandshakerBuilder {
223            address_book,
224            core_sync_svc,
225            protocol_request_svc_maker,
226            our_basic_node_data,
227            broadcast_stream_maker: new_broadcast_stream_maker,
228            connection_parent_span,
229            transport_client_config,
230            _zone: PhantomData,
231        }
232    }
233
234    /// Changes the parent [`Span`] of the connection task to the one provided.
235    ///
236    /// ## Default Connection Parent Span
237    ///
238    /// The default connection span will be [`Span::none`].
239    #[must_use]
240    pub fn with_connection_parent_span(self, connection_parent_span: Span) -> Self {
241        Self {
242            connection_parent_span: Some(connection_parent_span),
243            ..self
244        }
245    }
246
247    /// Builds the [`HandShaker`].
248    pub fn build(self) -> HandShaker<N, T, AdrBook, CSync, ProtoHdlr, BrdcstStrmMkr> {
249        HandShaker::new(
250            self.address_book,
251            self.core_sync_svc,
252            self.protocol_request_svc_maker,
253            self.broadcast_stream_maker,
254            self.our_basic_node_data,
255            self.connection_parent_span.unwrap_or(Span::none()),
256            self.transport_client_config,
257        )
258    }
259}