From 98dfaa4870f23b185239cbc83a5398395b896c8f Mon Sep 17 00:00:00 2001
From: "hinto.janai" <hinto.janai@protonmail.com>
Date: Thu, 17 Oct 2024 17:13:37 -0400
Subject: [PATCH] docs, `ConnectionInfo`, `AddressType`

---
 .../cuprated/src/rpc/request/address_book.rs  | 69 +++++++------
 p2p/p2p-core/src/services.rs                  |  9 +-
 p2p/p2p-core/src/types.rs                     | 73 ++++++++++++--
 rpc/types/src/misc/address_type.rs            | 97 +++++++++++++++++++
 rpc/types/src/misc/misc.rs                    |  2 +-
 rpc/types/src/misc/mod.rs                     |  2 +
 6 files changed, 213 insertions(+), 39 deletions(-)
 create mode 100644 rpc/types/src/misc/address_type.rs

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;