cuprate_p2p_core/
lib.rs

1//! # Cuprate P2P Core
2//!
3//! This crate is general purpose P2P networking library for working with Monero. This is a low level
4//! crate, which means it may seem verbose for a lot of use cases, if you want a crate that handles
5//! more of the P2P logic have a look at `cuprate-p2p`.
6//!
7//! # Network Zones
8//!
9//! This crate abstracts over network zones, Tor/I2p/clearnet with the [`NetworkZone`] trait. Currently only clearnet is implemented: [`ClearNet`].
10//!
11//! # Usage
12//!
13//! ## Connecting to a peer
14//!
15//! ```rust
16//! # use std::{net::SocketAddr, str::FromStr};
17//! #
18//! # use tower::ServiceExt;
19//! #
20//! # use cuprate_p2p_core::{
21//! #    client::{ConnectRequest, Connector, HandshakerBuilder},
22//! #    ClearNet, Network,
23//! # };
24//! # use cuprate_wire::{common::PeerSupportFlags, BasicNodeData};
25//! # use cuprate_test_utils::monerod::monerod;
26//! #
27//! # tokio_test::block_on(async move {
28//! #
29//! # let _monerod = monerod::<&str>([]).await;
30//! # let addr = _monerod.p2p_addr();
31//! #
32//! // The information about our local node.
33//! let our_basic_node_data = BasicNodeData {
34//!     my_port: 0,
35//!     network_id: Network::Mainnet.network_id(),
36//!     peer_id: 0,
37//!     support_flags: PeerSupportFlags::FLUFFY_BLOCKS,
38//!     rpc_port: 0,
39//!     rpc_credits_per_hash: 0,
40//! };
41//!
42//! // See [`HandshakerBuilder`] for information about the default values set, they may not be
43//! // appropriate for every use case.
44//! let handshaker = HandshakerBuilder::<ClearNet>::new(our_basic_node_data).build();
45//!
46//! // The outbound connector.
47//! let mut connector = Connector::new(handshaker);
48//!
49//! // The connection.
50//! let connection = connector
51//!     .oneshot(ConnectRequest {
52//!         addr,
53//!         permit: None,
54//!     })
55//!     .await
56//!     .unwrap();
57//! # });
58//! ```
59
60cfg_if::cfg_if! {
61    // Used in `tests/`
62    if #[cfg(test)] {
63        use cuprate_test_utils as _;
64        use tokio_test as _;
65        use hex as _;
66    }
67}
68
69use std::{fmt::Debug, hash::Hash};
70
71use futures::{Sink, Stream};
72
73use cuprate_wire::{
74    levin::LevinMessage, network_address::NetworkAddressIncorrectZone, BucketError, Message,
75    NetworkAddress,
76};
77
78pub mod client;
79mod constants;
80pub mod error;
81pub mod handles;
82mod network_zones;
83pub mod protocol;
84pub mod services;
85pub mod types;
86
87pub use error::*;
88pub use network_zones::{ClearNet, ClearNetServerCfg};
89pub use protocol::*;
90use services::*;
91//re-export
92pub use cuprate_helper::network::Network;
93pub use cuprate_wire::CoreSyncData;
94
95/// The direction of a connection.
96#[derive(Debug, Copy, Clone, Eq, PartialEq)]
97pub enum ConnectionDirection {
98    /// An inbound connection to our node.
99    Inbound,
100    /// An outbound connection from our node.
101    Outbound,
102}
103
104/// An address on a specific [`NetworkZone`].
105pub trait NetZoneAddress:
106    TryFrom<NetworkAddress, Error = NetworkAddressIncorrectZone>
107    + Into<NetworkAddress>
108    + std::fmt::Display
109    + Hash
110    + Eq
111    + Copy
112    + Send
113    + Sync
114    + Unpin
115    + 'static
116{
117    /// Cuprate needs to be able to ban peers by IP addresses and not just by `SocketAddr` as
118    /// that include the port, to be able to facilitate this network addresses must have a ban ID
119    /// which for hidden services could just be the address it self but for clear net addresses will
120    /// be the IP address.
121    ///
122    /// - TODO: IP zone banning?
123    /// - TODO: rename this to Host.
124    type BanID: Debug + Hash + Eq + Clone + Copy + Send + 'static;
125
126    /// Changes the port of this address to `port`.
127    fn set_port(&mut self, port: u16);
128
129    /// Turns this address into its canonical form.
130    fn make_canonical(&mut self);
131
132    /// Returns the [`Self::BanID`] for this address.
133    fn ban_id(&self) -> Self::BanID;
134
135    fn should_add_to_peer_list(&self) -> bool;
136}
137
138/// An abstraction over a network zone (tor/i2p/clear)
139#[async_trait::async_trait]
140pub trait NetworkZone: Clone + Copy + Send + 'static {
141    /// The network name.
142    const NAME: &'static str;
143    /// Check if our node ID matches the incoming peers node ID for this network.
144    ///
145    /// This has privacy implications on an anonymity network if true so should be set
146    /// to false.
147    const CHECK_NODE_ID: bool;
148
149    /// The address type of this network.
150    type Addr: NetZoneAddress;
151
152    /// The stream (incoming data) type for this network.
153    type Stream: Stream<Item = Result<Message, BucketError>> + Unpin + Send + 'static;
154    /// The sink (outgoing data) type for this network.
155    type Sink: Sink<LevinMessage<Message>, Error = BucketError> + Unpin + Send + 'static;
156    /// The inbound connection listener for this network.
157    type Listener: Stream<Item = Result<(Option<Self::Addr>, Self::Stream, Self::Sink), std::io::Error>>
158        + Send
159        + 'static;
160    /// Config used to start a server which listens for incoming connections.
161    type ServerCfg: Clone + Debug + Send + 'static;
162
163    /// Connects to a peer with the given address.
164    ///
165    /// <div class="warning">    
166    ///
167    /// This does not complete a handshake with the peer, to do that see the [crate](crate) docs.
168    ///
169    /// </div>
170    ///
171    /// Returns the [`Self::Stream`] and [`Self::Sink`] to send messages to the peer.
172    async fn connect_to_peer(
173        addr: Self::Addr,
174    ) -> Result<(Self::Stream, Self::Sink), std::io::Error>;
175
176    async fn incoming_connection_listener(
177        config: Self::ServerCfg,
178        port: u16,
179    ) -> Result<Self::Listener, std::io::Error>;
180}
181
182// ####################################################################################
183// Below here is just helper traits, so we don't have to type out tower::Service bounds
184// everywhere but still get to use tower.
185
186pub trait AddressBook<Z: NetworkZone>:
187    tower::Service<
188        AddressBookRequest<Z>,
189        Response = AddressBookResponse<Z>,
190        Error = tower::BoxError,
191        Future: Send + 'static,
192    > + Send
193    + 'static
194{
195}
196
197impl<T, Z: NetworkZone> AddressBook<Z> for T where
198    T: tower::Service<
199            AddressBookRequest<Z>,
200            Response = AddressBookResponse<Z>,
201            Error = tower::BoxError,
202            Future: Send + 'static,
203        > + Send
204        + 'static
205{
206}
207
208pub trait CoreSyncSvc:
209    tower::Service<
210        CoreSyncDataRequest,
211        Response = CoreSyncDataResponse,
212        Error = tower::BoxError,
213        Future: Send + 'static,
214    > + Send
215    + 'static
216{
217}
218
219impl<T> CoreSyncSvc for T where
220    T: tower::Service<
221            CoreSyncDataRequest,
222            Response = CoreSyncDataResponse,
223            Error = tower::BoxError,
224            Future: Send + 'static,
225        > + Send
226        + 'static
227{
228}
229
230pub trait ProtocolRequestHandler:
231    tower::Service<
232        ProtocolRequest,
233        Response = ProtocolResponse,
234        Error = tower::BoxError,
235        Future: Send + 'static,
236    > + Send
237    + 'static
238{
239}
240
241impl<T> ProtocolRequestHandler for T where
242    T: tower::Service<
243            ProtocolRequest,
244            Response = ProtocolResponse,
245            Error = tower::BoxError,
246            Future: Send + 'static,
247        > + Send
248        + 'static
249{
250}
251
252pub trait ProtocolRequestHandlerMaker<Z: NetworkZone>:
253    tower::MakeService<
254        client::PeerInformation<Z::Addr>,
255        ProtocolRequest,
256        MakeError = tower::BoxError,
257        Service: ProtocolRequestHandler,
258        Future: Send + 'static,
259    > + Send
260    + 'static
261{
262}
263
264impl<T, Z: NetworkZone> ProtocolRequestHandlerMaker<Z> for T where
265    T: tower::MakeService<
266            client::PeerInformation<Z::Addr>,
267            ProtocolRequest,
268            MakeError = tower::BoxError,
269            Service: ProtocolRequestHandler,
270            Future: Send + 'static,
271        > + Send
272        + 'static
273{
274}