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