cuprate_address_book/
lib.rs

1//! Cuprate Address Book
2//!
3//! This module holds the logic for persistent peer storage.
4//! Cuprates address book is modeled as a [`tower::Service`]
5//! The request is [`AddressBookRequest`](cuprate_p2p_core::services::AddressBookRequest) and the response is
6//! [`AddressBookResponse`](cuprate_p2p_core::services::AddressBookResponse).
7//!
8//! Cuprate, like monerod, actually has multiple address books, one
9//! for each [`NetworkZone`]. This is to reduce the possibility of
10//! clear net peers getting linked to their dark counterparts
11//! and so peers will only get told about peers they can
12//! connect to.
13use std::{io::ErrorKind, path::PathBuf, time::Duration};
14
15use cuprate_p2p_core::{NetZoneAddress, NetworkZone};
16
17mod book;
18mod peer_list;
19mod store;
20
21/// The address book config.
22#[derive(Debug, Clone)]
23pub struct AddressBookConfig<Z: NetworkZone> {
24    /// The maximum number of white peers in the peer list.
25    ///
26    /// White peers are peers we have connected to before.
27    pub max_white_list_length: usize,
28    /// The maximum number of gray peers in the peer list.
29    ///
30    /// Gray peers are peers we are yet to make a connection to.
31    pub max_gray_list_length: usize,
32    /// The location to store the peer store files.
33    pub peer_store_directory: PathBuf,
34    /// The amount of time between saving the address book to disk.
35    pub peer_save_period: Duration,
36
37    /// Our own address to advertise to peers. (Only set if `Z::BROADCAST_OWN_ADDR` = `true`)
38    pub our_own_address: Option<Z::Addr>,
39}
40
41/// Possible errors when dealing with the address book.
42/// This is boxed when returning an error in the [`tower::Service`].
43#[derive(Debug, thiserror::Error, Eq, PartialEq)]
44pub enum AddressBookError {
45    /// The peer is already connected.
46    #[error("Peer is already connected")]
47    PeerAlreadyConnected,
48    /// The peer is not in the address book for this zone.
49    #[error("Peer was not found in book")]
50    PeerNotFound,
51    /// Immutable peer data was changed.
52    #[error("Immutable peer data was changed: {0}")]
53    PeersDataChanged(&'static str),
54    /// The peer is banned.
55    #[error("The peer is banned")]
56    PeerIsBanned,
57    /// The channel to the address book has closed unexpectedly.
58    #[error("The address books channel has closed.")]
59    AddressBooksChannelClosed,
60    /// The address book task has exited.
61    #[error("The address book task has exited.")]
62    AddressBookTaskExited,
63}
64
65/// Initializes the P2P address book for a specific network zone.
66pub async fn init_address_book<Z: BorshNetworkZone>(
67    cfg: AddressBookConfig<Z>,
68) -> Result<book::AddressBook<Z>, std::io::Error> {
69    let (white_list, gray_list) = match store::read_peers_from_disk::<Z>(&cfg).await {
70        Ok(res) => res,
71        Err(e) if e.kind() == ErrorKind::NotFound => (vec![], vec![]),
72        Err(e) => {
73            tracing::error!(
74                "Error: Failed to open peer list,\n{},\nstarting with an empty list",
75                e
76            );
77            (vec![], vec![])
78        }
79    };
80
81    let address_book = book::AddressBook::<Z>::new(cfg, white_list, gray_list, Vec::new());
82
83    Ok(address_book)
84}
85
86use sealed::BorshNetworkZone;
87mod sealed {
88    use super::*;
89
90    /// An internal trait for the address book for a [`NetworkZone`] that adds the requirement of [`borsh`] traits
91    /// onto the network address.
92    pub trait BorshNetworkZone: NetworkZone<Addr = Self::BorshAddr> {
93        type BorshAddr: NetZoneAddress + borsh::BorshDeserialize + borsh::BorshSerialize;
94    }
95
96    impl<T: NetworkZone> BorshNetworkZone for T
97    where
98        T::Addr: borsh::BorshDeserialize + borsh::BorshSerialize,
99    {
100        type BorshAddr = T::Addr;
101    }
102}