diff --git a/binaries/cuprated/src/rpc/request/address_book.rs b/binaries/cuprated/src/rpc/request/address_book.rs index bd46220..c3ffbd3 100644 --- a/binaries/cuprated/src/rpc/request/address_book.rs +++ b/binaries/cuprated/src/rpc/request/address_book.rs @@ -52,32 +52,45 @@ pub(crate) async fn connection_info<Z: NetworkZone>( // FIXME: impl this map somewhere instead of inline. let vec = vec .into_iter() - .map(|info| ConnectionInfo { - address: info.address.to_string(), - address_type: info.address_type, - avg_download: info.avg_download, - avg_upload: info.avg_upload, - connection_id: info.connection_id, - current_download: info.current_download, - current_upload: info.current_upload, - height: info.height, - host: info.host, - incoming: info.incoming, - ip: info.ip, - live_time: info.live_time, - localhost: info.localhost, - local_ip: info.local_ip, - peer_id: info.peer_id, - port: info.port, - pruning_seed: info.pruning_seed, - recv_count: info.recv_count, - recv_idle_time: info.recv_idle_time, - rpc_credits_per_hash: info.rpc_credits_per_hash, - rpc_port: info.rpc_port, - send_count: info.send_count, - send_idle_time: info.send_idle_time, - state: info.state, - support_flags: info.support_flags, + .map(|info| { + use cuprate_p2p_core::types::AddressType as A1; + use cuprate_rpc_types::misc::AddressType as A2; + + let address_type = match info.address_type { + A1::Invalid => A2::Invalid, + A1::Ipv4 => A2::Ipv4, + A1::Ipv6 => A2::Ipv6, + A1::I2p => A2::I2p, + A1::Tor => A2::Tor, + }; + + ConnectionInfo { + address: info.address.to_string(), + address_type, + avg_download: info.avg_download, + avg_upload: info.avg_upload, + connection_id: hex::encode(info.connection_id.to_ne_bytes()), + current_download: info.current_download, + current_upload: info.current_upload, + height: info.height, + host: info.host, + incoming: info.incoming, + ip: info.ip, + live_time: info.live_time, + localhost: info.localhost, + local_ip: info.local_ip, + peer_id: info.peer_id, + port: info.port, + pruning_seed: info.pruning_seed.compress(), + recv_count: info.recv_count, + recv_idle_time: info.recv_idle_time, + rpc_credits_per_hash: info.rpc_credits_per_hash, + rpc_port: info.rpc_port, + send_count: info.send_count, + send_idle_time: info.send_idle_time, + state: info.state, + support_flags: info.support_flags, + } }) .collect(); @@ -177,10 +190,10 @@ pub(crate) async fn spans<Z: NetworkZone>( let vec = vec .into_iter() .map(|span| Span { - connection_id: span.connection_id, + connection_id: hex::encode(span.connection_id.to_ne_bytes()), nblocks: span.nblocks, rate: span.rate, - remote_address: span.remote_address, + remote_address: span.remote_address.to_string(), size: span.size, speed: span.speed, start_block_height: span.start_block_height, diff --git a/p2p/p2p-core/src/services.rs b/p2p/p2p-core/src/services.rs index 6124e3e..481f024 100644 --- a/p2p/p2p-core/src/services.rs +++ b/p2p/p2p-core/src/services.rs @@ -133,10 +133,13 @@ pub enum AddressBookRequest<Z: NetworkZone> { /// Get the state of all bans. GetBans, - /// TODO + /// Get [`Span`] data. + /// + /// This is data that describes an active downloading process, + /// if we are fully synced, this will return an empty [`Vec`]. Spans, - /// TODO + /// Get the next [`PruningSeed`] needed for a pruned sync. NextNeededPruningSeed, } @@ -177,7 +180,7 @@ pub enum AddressBookResponse<Z: NetworkZone> { GetBans(Vec<BanState<Z::Addr>>), /// Response to [`AddressBookRequest::Spans`]. - Spans(Vec<Span>), + Spans(Vec<Span<Z::Addr>>), /// Response to [`AddressBookRequest::NextNeededPruningSeed`]. NextNeededPruningSeed(PruningSeed), diff --git a/p2p/p2p-core/src/types.rs b/p2p/p2p-core/src/types.rs index 4714f9e..eef3cb8 100644 --- a/p2p/p2p-core/src/types.rs +++ b/p2p/p2p-core/src/types.rs @@ -2,6 +2,8 @@ use std::time::{Duration, Instant}; +use cuprate_pruning::PruningSeed; + use crate::NetZoneAddress; /// Data within [`crate::services::AddressBookRequest::SetBan`]. @@ -22,15 +24,72 @@ pub struct BanState<A: NetZoneAddress> { pub unban_instant: Option<Instant>, } +/// An enumeration of address types. +/// +/// Used [`ConnectionInfo::address_type`]. +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(u8)] +pub enum AddressType { + #[default] + Invalid, + Ipv4, + Ipv6, + I2p, + Tor, +} + +impl AddressType { + /// Convert [`Self`] to a [`u8`]. + /// + /// ```rust + /// use cuprate_p2p_core::AddressType as A; + /// + /// assert_eq!(A::Invalid.to_u8(), 0); + /// assert_eq!(A::Ipv4.to_u8(), 1); + /// assert_eq!(A::Ipv6.to_u8(), 2); + /// assert_eq!(A::I2p.to_u8(), 3); + /// assert_eq!(A::Tor.to_u8(), 4); + /// ``` + pub const fn to_u8(self) -> u8 { + self as u8 + } + + /// Convert a [`u8`] to a [`Self`]. + /// + /// # Errors + /// This returns [`None`] if `u > 4`. + /// + /// ```rust + /// use cuprate_p2p_core::AddressType as A; + /// + /// assert_eq!(A::from_u8(0), Some(A::Invalid)); + /// assert_eq!(A::from_u8(1), Some(A::Ipv4)); + /// assert_eq!(A::from_u8(2), Some(A::Ipv6)); + /// assert_eq!(A::from_u8(3), Some(A::I2p)); + /// assert_eq!(A::from_u8(4), Some(A::Tor)); + /// assert_eq!(A::from_u8(5), None); + /// ``` + pub const fn from_u8(u: u8) -> Option<Self> { + Some(match u { + 0 => Self::Invalid, + 1 => Self::Ipv4, + 2 => Self::Ipv6, + 3 => Self::I2p, + 4 => Self::Tor, + _ => return None, + }) + } +} + // TODO: reduce fields and map to RPC type. // /// Data within [`crate::services::AddressBookResponse::ConnectionInfo`]. pub struct ConnectionInfo<A: NetZoneAddress> { pub address: A, - pub address_type: u8, + pub address_type: AddressType, pub avg_download: u64, pub avg_upload: u64, - pub connection_id: String, + pub connection_id: u64, // TODO: boost::uuids::uuid pub current_download: u64, pub current_upload: u64, pub height: u64, @@ -42,14 +101,14 @@ pub struct ConnectionInfo<A: NetZoneAddress> { pub local_ip: bool, pub peer_id: String, pub port: String, - pub pruning_seed: u32, + pub pruning_seed: PruningSeed, pub recv_count: u64, pub recv_idle_time: u64, pub rpc_credits_per_hash: u32, pub rpc_port: u16, pub send_count: u64, pub send_idle_time: u64, - pub state: String, + pub state: String, // TODO: what type is this? pub support_flags: u32, } @@ -57,11 +116,11 @@ pub struct ConnectionInfo<A: NetZoneAddress> { /// /// Data within [`crate::services::AddressBookResponse::Spans`]. #[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Span { - pub connection_id: String, +pub struct Span<A: NetZoneAddress> { + pub connection_id: u64, // TODO: boost::uuids::uuid pub nblocks: u64, pub rate: u32, - pub remote_address: String, + pub remote_address: A, pub size: u64, pub speed: u32, pub start_block_height: u64, diff --git a/rpc/types/src/misc/address_type.rs b/rpc/types/src/misc/address_type.rs new file mode 100644 index 0000000..ca9f837 --- /dev/null +++ b/rpc/types/src/misc/address_type.rs @@ -0,0 +1,97 @@ +//! Types of network addresses; used in P2P. + +use cuprate_epee_encoding::Marker; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +#[cfg(feature = "epee")] +use cuprate_epee_encoding::{ + error, + macros::bytes::{Buf, BufMut}, + EpeeValue, +}; + +/// Used in [`crate::misc::ConnectionInfo::address_type`]. +#[doc = crate::macros::monero_definition_link!( + cc73fe71162d564ffda8e549b79a350bca53c454, + "epee/include/net/enums.h", + 39..=47 +)] +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "serde", serde(untagged))] +#[repr(u8)] +pub enum AddressType { + #[default] + Invalid, + Ipv4, + Ipv6, + I2p, + Tor, +} + +impl AddressType { + /// Convert [`Self`] to a [`u8`]. + /// + /// ```rust + /// use cuprate_rpc_types::misc::AddressType as A; + /// + /// assert_eq!(A::Invalid.to_u8(), 0); + /// assert_eq!(A::Ipv4.to_u8(), 1); + /// assert_eq!(A::Ipv6.to_u8(), 2); + /// assert_eq!(A::I2p.to_u8(), 3); + /// assert_eq!(A::Tor.to_u8(), 4); + /// ``` + pub const fn to_u8(self) -> u8 { + self as u8 + } + + /// Convert a [`u8`] to a [`Self`]. + /// + /// # Errors + /// This returns [`None`] if `u > 4`. + /// + /// ```rust + /// use cuprate_rpc_types::misc::AddressType as A; + /// + /// assert_eq!(A::from_u8(0), Some(A::Invalid)); + /// assert_eq!(A::from_u8(1), Some(A::Ipv4)); + /// assert_eq!(A::from_u8(2), Some(A::Ipv6)); + /// assert_eq!(A::from_u8(3), Some(A::I2p)); + /// assert_eq!(A::from_u8(4), Some(A::Tor)); + /// assert_eq!(A::from_u8(5), None); + /// ``` + pub const fn from_u8(u: u8) -> Option<Self> { + Some(match u { + 0 => Self::Invalid, + 1 => Self::Ipv4, + 2 => Self::Ipv6, + 3 => Self::I2p, + 4 => Self::Tor, + _ => return None, + }) + } +} + +impl From<AddressType> for u8 { + fn from(value: AddressType) -> Self { + value.to_u8() + } +} + +#[cfg(feature = "epee")] +impl EpeeValue for AddressType { + const MARKER: Marker = u8::MARKER; + + fn read<B: Buf>(r: &mut B, marker: &Marker) -> error::Result<Self> { + let u = u8::read(r, marker)?; + Self::from_u8(u).ok_or(error::Error::Format("u8 was greater than 4")) + } + + fn write<B: BufMut>(self, w: &mut B) -> error::Result<()> { + let u = self.to_u8(); + u8::write(u, w)?; + Ok(()) + } +} diff --git a/rpc/types/src/misc/misc.rs b/rpc/types/src/misc/misc.rs index 842997b..49fed6f 100644 --- a/rpc/types/src/misc/misc.rs +++ b/rpc/types/src/misc/misc.rs @@ -110,7 +110,7 @@ define_struct_and_impl_epee! { /// Used in [`crate::json::GetConnectionsResponse`]. ConnectionInfo { address: String, - address_type: u8, + address_type: crate::misc::AddressType, avg_download: u64, avg_upload: u64, connection_id: String, diff --git a/rpc/types/src/misc/mod.rs b/rpc/types/src/misc/mod.rs index e09f847..672d493 100644 --- a/rpc/types/src/misc/mod.rs +++ b/rpc/types/src/misc/mod.rs @@ -12,6 +12,7 @@ )] //---------------------------------------------------------------------------------------------------- Mod +mod address_type; mod binary_string; mod distribution; mod key_image_spent_status; @@ -21,6 +22,7 @@ mod pool_info_extent; mod status; mod tx_entry; +pub use address_type::AddressType; pub use binary_string::BinaryString; pub use distribution::{Distribution, DistributionCompressedBinary, DistributionUncompressed}; pub use key_image_spent_status::KeyImageSpentStatus;