cuprated/rpc/service/
address_book.rs

1//! Functions to send [`AddressBookRequest`]s.
2
3use std::net::SocketAddrV4;
4
5use anyhow::{anyhow, Error};
6use tower::ServiceExt;
7
8use cuprate_helper::{cast::usize_to_u64, map::u32_from_ipv4};
9use cuprate_p2p_core::{
10    services::{AddressBookRequest, AddressBookResponse, ZoneSpecificPeerListEntryBase},
11    types::{BanState, ConnectionId},
12    AddressBook, NetworkZone,
13};
14use cuprate_rpc_types::misc::ConnectionInfo;
15use cuprate_types::rpc::Peer;
16
17// FIXME: use `anyhow::Error` over `tower::BoxError` in address book.
18
19/// [`AddressBookRequest::PeerlistSize`]
20pub async fn peerlist_size<Z: NetworkZone>(
21    address_book: &mut impl AddressBook<Z>,
22) -> Result<(u64, u64), Error> {
23    let AddressBookResponse::PeerlistSize { white, grey } = address_book
24        .ready()
25        .await
26        .map_err(|e| anyhow!(e))?
27        .call(AddressBookRequest::PeerlistSize)
28        .await
29        .map_err(|e| anyhow!(e))?
30    else {
31        unreachable!();
32    };
33
34    Ok((usize_to_u64(white), usize_to_u64(grey)))
35}
36
37/// [`AddressBookRequest::ConnectionInfo`]
38pub async fn connection_info<Z: NetworkZone>(
39    address_book: &mut impl AddressBook<Z>,
40) -> Result<Vec<ConnectionInfo>, Error> {
41    let AddressBookResponse::ConnectionInfo(vec) = address_book
42        .ready()
43        .await
44        .map_err(|e| anyhow!(e))?
45        .call(AddressBookRequest::ConnectionInfo)
46        .await
47        .map_err(|e| anyhow!(e))?
48    else {
49        unreachable!();
50    };
51
52    // FIXME: impl this map somewhere instead of inline.
53    let vec = vec
54        .into_iter()
55        .map(|info| {
56            let (ip, port) = match info.socket_addr {
57                Some(socket) => (socket.ip().to_string(), socket.port().to_string()),
58                None => (String::new(), String::new()),
59            };
60
61            ConnectionInfo {
62                address: info.address.to_string(),
63                address_type: info.address_type,
64                avg_download: info.avg_download,
65                avg_upload: info.avg_upload,
66                connection_id: String::from(ConnectionId::DEFAULT_STR),
67                current_download: info.current_download,
68                current_upload: info.current_upload,
69                height: info.height,
70                host: info.host,
71                incoming: info.incoming,
72                ip,
73                live_time: info.live_time,
74                localhost: info.localhost,
75                local_ip: info.local_ip,
76                peer_id: hex::encode(info.peer_id.to_ne_bytes()),
77                port,
78                pruning_seed: info.pruning_seed.compress(),
79                recv_count: info.recv_count,
80                recv_idle_time: info.recv_idle_time,
81                rpc_credits_per_hash: info.rpc_credits_per_hash,
82                rpc_port: info.rpc_port,
83                send_count: info.send_count,
84                send_idle_time: info.send_idle_time,
85                state: info.state,
86                support_flags: info.support_flags,
87            }
88        })
89        .collect();
90
91    Ok(vec)
92}
93
94/// [`AddressBookRequest::ConnectionCount`]
95pub async fn connection_count<Z: NetworkZone>(
96    address_book: &mut impl AddressBook<Z>,
97) -> Result<(u64, u64), Error> {
98    let AddressBookResponse::ConnectionCount { incoming, outgoing } = address_book
99        .ready()
100        .await
101        .map_err(|e| anyhow!(e))?
102        .call(AddressBookRequest::ConnectionCount)
103        .await
104        .map_err(|e| anyhow!(e))?
105    else {
106        unreachable!();
107    };
108
109    Ok((usize_to_u64(incoming), usize_to_u64(outgoing)))
110}
111
112/// [`AddressBookRequest::SetBan`]
113pub async fn set_ban<Z: NetworkZone>(
114    address_book: &mut impl AddressBook<Z>,
115    set_ban: cuprate_p2p_core::types::SetBan<Z::Addr>,
116) -> Result<(), Error> {
117    let AddressBookResponse::Ok = address_book
118        .ready()
119        .await
120        .map_err(|e| anyhow!(e))?
121        .call(AddressBookRequest::SetBan(set_ban))
122        .await
123        .map_err(|e| anyhow!(e))?
124    else {
125        unreachable!();
126    };
127
128    Ok(())
129}
130
131/// [`AddressBookRequest::GetBan`]
132pub async fn get_ban<Z: NetworkZone>(
133    address_book: &mut impl AddressBook<Z>,
134    peer: Z::Addr,
135) -> Result<Option<std::time::Instant>, Error> {
136    let AddressBookResponse::GetBan { unban_instant } = address_book
137        .ready()
138        .await
139        .map_err(|e| anyhow!(e))?
140        .call(AddressBookRequest::GetBan(peer))
141        .await
142        .map_err(|e| anyhow!(e))?
143    else {
144        unreachable!();
145    };
146
147    Ok(unban_instant)
148}
149
150/// [`AddressBookRequest::GetBans`]
151pub async fn get_bans<Z: NetworkZone>(
152    address_book: &mut impl AddressBook<Z>,
153) -> Result<Vec<BanState<Z::Addr>>, Error> {
154    let AddressBookResponse::GetBans(bans) = address_book
155        .ready()
156        .await
157        .map_err(|e| anyhow!(e))?
158        .call(AddressBookRequest::GetBans)
159        .await
160        .map_err(|e| anyhow!(e))?
161    else {
162        unreachable!();
163    };
164
165    Ok(bans)
166}
167
168/// [`AddressBookRequest::Peerlist`]
169pub async fn peerlist<Z: NetworkZone>(
170    address_book: &mut impl AddressBook<Z>,
171) -> Result<(Vec<Peer>, Vec<Peer>), Error> {
172    let AddressBookResponse::Peerlist(peerlist) = address_book
173        .ready()
174        .await
175        .map_err(|e| anyhow!(e))?
176        .call(AddressBookRequest::Peerlist)
177        .await
178        .map_err(|e| anyhow!(e))?
179    else {
180        unreachable!();
181    };
182
183    fn map<Z: NetworkZone>(peers: Vec<ZoneSpecificPeerListEntryBase<Z::Addr>>) -> Vec<Peer> {
184        peers
185            .into_iter()
186            .map(|peer| {
187                let ZoneSpecificPeerListEntryBase {
188                    adr,
189                    id,
190                    last_seen,
191                    pruning_seed,
192                    rpc_port,
193                    rpc_credits_per_hash,
194                } = peer;
195
196                let host = adr.to_string();
197
198                let (ip, port) = if let Ok(socket_addr) = host.parse::<SocketAddrV4>() {
199                    (u32_from_ipv4(*socket_addr.ip()), socket_addr.port())
200                } else {
201                    (0, 0)
202                };
203
204                let last_seen = last_seen.try_into().unwrap_or(0);
205                let pruning_seed = pruning_seed.compress();
206
207                Peer {
208                    id,
209                    host,
210                    ip,
211                    port,
212                    rpc_port,
213                    rpc_credits_per_hash,
214                    last_seen,
215                    pruning_seed,
216                }
217            })
218            .collect()
219    }
220
221    let white = map::<Z>(peerlist.white);
222    let grey = map::<Z>(peerlist.grey);
223
224    Ok((white, grey))
225}