Compare commits

...

2 commits

Author SHA1 Message Date
hinto.janai
afab072816
cuprated/p2p: fix ConnectionInfo 2024-10-23 17:06:19 -04:00
hinto.janai
b42a110c98
remove BlockchainManagerRequest::Overview 2024-10-23 16:37:17 -04:00
9 changed files with 209 additions and 57 deletions

View file

@ -3,6 +3,7 @@
//! Will contain the code to initiate the RPC and a request handler. //! Will contain the code to initiate the RPC and a request handler.
mod bin; mod bin;
mod constants;
mod handler; mod handler;
mod json; mod json;
mod other; mod other;

View file

@ -0,0 +1,5 @@
//! Constants used within RPC.
/// The string message used in RPC response fields for when
/// `cuprated` does not support a field that `monerod` has.
pub(super) const FIELD_NOT_SUPPORTED: &str = "`cuprated` does not support this field.";

View file

@ -97,20 +97,6 @@ pub enum BlockchainManagerRequest {
/// The address that will receive the coinbase reward. /// The address that will receive the coinbase reward.
wallet_address: String, wallet_address: String,
}, },
/// Get a visual [`String`] overview of blockchain progress.
///
/// This is a highly implementation specific format used by
/// `monerod` in the `sync_info` RPC call's `overview` field;
/// it is essentially an ASCII visual of blocks.
///
/// See also:
/// - <https://www.getmonero.org/resources/developer-guides/daemon-rpc.html#sync_info>
/// - <https://github.com/monero-project/monero/blob/master/src/cryptonote_protocol/block_queue.cpp#L178>
Overview {
/// TODO: the current blockchain height? do we need to pass this?
height: usize,
},
} }
/// TODO: use real type when public. /// TODO: use real type when public.
@ -157,9 +143,6 @@ pub enum BlockchainManagerResponse {
/// The new top height. (TODO: is this correct?) /// The new top height. (TODO: is this correct?)
height: usize, height: usize,
}, },
/// Response to [`BlockchainManagerRequest::Overview`]
Overview(String),
} }
/// TODO: use real type when public. /// TODO: use real type when public.

View file

@ -14,6 +14,8 @@ use cuprate_p2p_core::{
AddressBook, NetworkZone, AddressBook, NetworkZone,
}; };
use crate::rpc::constants::FIELD_NOT_SUPPORTED;
// FIXME: use `anyhow::Error` over `tower::BoxError` in address book. // FIXME: use `anyhow::Error` over `tower::BoxError` in address book.
/// [`AddressBookRequest::PeerlistSize`] /// [`AddressBookRequest::PeerlistSize`]
@ -53,15 +55,20 @@ pub(crate) async fn connection_info<Z: NetworkZone>(
let vec = vec let vec = vec
.into_iter() .into_iter()
.map(|info| { .map(|info| {
use cuprate_p2p_core::types::AddressType as A1; /// Message to use when casting between enums with `u8` fails.
use cuprate_rpc_types::misc::AddressType as A2; /// This should never happen.
const EXPECT: &str = "u8 repr between these types should be 1-1";
let address_type = match info.address_type { let address_type =
A1::Invalid => A2::Invalid, cuprate_rpc_types::misc::AddressType::from_u8(info.address_type.to_u8())
A1::Ipv4 => A2::Ipv4, .expect(EXPECT);
A1::Ipv6 => A2::Ipv6,
A1::I2p => A2::I2p, let state = cuprate_rpc_types::misc::ConnectionState::from_u8(info.state.to_u8())
A1::Tor => A2::Tor, .expect(EXPECT);
let (ip, port) = match info.socket_addr {
Some(socket) => (socket.ip().to_string(), socket.port().to_string()),
None => (String::new(), String::new()),
}; };
ConnectionInfo { ConnectionInfo {
@ -69,18 +76,18 @@ pub(crate) async fn connection_info<Z: NetworkZone>(
address_type, address_type,
avg_download: info.avg_download, avg_download: info.avg_download,
avg_upload: info.avg_upload, avg_upload: info.avg_upload,
connection_id: hex::encode(info.connection_id.to_ne_bytes()), connection_id: String::from(FIELD_NOT_SUPPORTED),
current_download: info.current_download, current_download: info.current_download,
current_upload: info.current_upload, current_upload: info.current_upload,
height: info.height, height: info.height,
host: info.host, host: info.host,
incoming: info.incoming, incoming: info.incoming,
ip: info.ip, ip,
live_time: info.live_time, live_time: info.live_time,
localhost: info.localhost, localhost: info.localhost,
local_ip: info.local_ip, local_ip: info.local_ip,
peer_id: info.peer_id, peer_id: hex::encode(info.peer_id.to_ne_bytes()),
port: info.port, port,
pruning_seed: info.pruning_seed.compress(), pruning_seed: info.pruning_seed.compress(),
recv_count: info.recv_count, recv_count: info.recv_count,
recv_idle_time: info.recv_idle_time, recv_idle_time: info.recv_idle_time,
@ -88,7 +95,7 @@ pub(crate) async fn connection_info<Z: NetworkZone>(
rpc_port: info.rpc_port, rpc_port: info.rpc_port,
send_count: info.send_count, send_count: info.send_count,
send_idle_time: info.send_idle_time, send_idle_time: info.send_idle_time,
state: info.state, state,
support_flags: info.support_flags, support_flags: info.support_flags,
} }
}) })
@ -190,7 +197,7 @@ pub(crate) async fn spans<Z: NetworkZone>(
let vec = vec let vec = vec
.into_iter() .into_iter()
.map(|span| Span { .map(|span| Span {
connection_id: hex::encode(span.connection_id.to_ne_bytes()), connection_id: String::from(FIELD_NOT_SUPPORTED),
nblocks: span.nblocks, nblocks: span.nblocks,
rate: span.rate, rate: span.rate,
remote_address: span.remote_address.to_string(), remote_address: span.remote_address.to_string(),

View file

@ -214,22 +214,3 @@ pub(crate) async fn generate_blocks(
Ok((blocks, usize_to_u64(height))) Ok((blocks, usize_to_u64(height)))
} }
/// [`BlockchainManagerRequest::Overview`]
pub(crate) async fn overview(
blockchain_manager: &mut BlockchainManagerHandle,
height: u64,
) -> Result<String, Error> {
let BlockchainManagerResponse::Overview(overview) = blockchain_manager
.ready()
.await?
.call(BlockchainManagerRequest::Overview {
height: u64_to_usize(height),
})
.await?
else {
unreachable!();
};
Ok(overview)
}

View file

@ -81,26 +81,85 @@ impl AddressType {
} }
} }
/// An enumeration of P2P connection states.
///
/// Used [`ConnectionInfo::state`].
///
/// Original definition:
/// - <https://github.com/monero-project/monero/blob/893916ad091a92e765ce3241b94e706ad012b62a/src/cryptonote_basic/connection_context.h#L49>
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum ConnectionState {
BeforeHandshake,
Synchronizing,
Standby,
Idle,
#[default]
Normal,
}
impl ConnectionState {
/// Convert [`Self`] to a [`u8`].
///
/// ```rust
/// use cuprate_p2p_core::types::ConnectionState as C;
///
/// assert_eq!(C::BeforeHandshake.to_u8(), 0);
/// assert_eq!(C::Synchronizing.to_u8(), 1);
/// assert_eq!(C::Standby.to_u8(), 2);
/// assert_eq!(C::Idle.to_u8(), 3);
/// assert_eq!(C::Normal.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::types::ConnectionState as C;
///
/// assert_eq!(C::from_u8(0), Some(C::BeforeHandShake));
/// assert_eq!(C::from_u8(1), Some(C::Synchronizing));
/// assert_eq!(C::from_u8(2), Some(C::Standby));
/// assert_eq!(C::from_u8(3), Some(C::Idle));
/// assert_eq!(C::from_u8(4), Some(C::Normal));
/// assert_eq!(C::from_u8(5), None);
/// ```
pub const fn from_u8(u: u8) -> Option<Self> {
Some(match u {
0 => Self::BeforeHandshake,
1 => Self::Synchronizing,
2 => Self::Standby,
3 => Self::Idle,
4 => Self::Normal,
_ => return None,
})
}
}
// TODO: reduce fields and map to RPC type. // TODO: reduce fields and map to RPC type.
// //
/// Data within [`crate::services::AddressBookResponse::ConnectionInfo`]. /// Data within [`crate::services::AddressBookResponse::ConnectionInfo`].
pub struct ConnectionInfo<A: NetZoneAddress> { pub struct ConnectionInfo<A: NetZoneAddress> {
// The following fields are mostly the same as `monerod`.
pub address: A, pub address: A,
pub address_type: AddressType, pub address_type: AddressType,
pub avg_download: u64, pub avg_download: u64,
pub avg_upload: u64, pub avg_upload: u64,
pub connection_id: u64, // TODO: boost::uuids::uuid
pub current_download: u64, pub current_download: u64,
pub current_upload: u64, pub current_upload: u64,
pub height: u64, pub height: u64,
/// Either a domain or an IP without the port.
pub host: String, pub host: String,
pub incoming: bool, pub incoming: bool,
pub ip: String,
pub live_time: u64, pub live_time: u64,
pub localhost: bool, pub localhost: bool,
pub local_ip: bool, pub local_ip: bool,
pub peer_id: String, pub peer_id: u64,
pub port: String,
pub pruning_seed: PruningSeed, pub pruning_seed: PruningSeed,
pub recv_count: u64, pub recv_count: u64,
pub recv_idle_time: u64, pub recv_idle_time: u64,
@ -108,8 +167,16 @@ pub struct ConnectionInfo<A: NetZoneAddress> {
pub rpc_port: u16, pub rpc_port: u16,
pub send_count: u64, pub send_count: u64,
pub send_idle_time: u64, pub send_idle_time: u64,
pub state: String, // TODO: what type is this? pub state: ConnectionState,
pub support_flags: u32, pub support_flags: u32,
// The following fields are slightly different than `monerod`.
//
/// [`None`] if Tor/i2p or unknown.
pub socket_addr: Option<std::net::SocketAddr>,
// This field does not exist for Cuprate:
// <https://github.com/Cuprate/cuprate/pull/320#discussion_r1811335020>
// pub connection_id: u128,
} }
/// Used in RPC's `sync_info`. /// Used in RPC's `sync_info`.
@ -117,7 +184,6 @@ pub struct ConnectionInfo<A: NetZoneAddress> {
/// Data within [`crate::services::AddressBookResponse::Spans`]. /// Data within [`crate::services::AddressBookResponse::Spans`].
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Span<A: NetZoneAddress> { pub struct Span<A: NetZoneAddress> {
pub connection_id: u64, // TODO: boost::uuids::uuid
pub nblocks: u64, pub nblocks: u64,
pub rate: u32, pub rate: u32,
pub remote_address: A, pub remote_address: A,

View file

@ -0,0 +1,107 @@
//! 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,
"cryptonote_basic/connection_context.h",
49..=56
)]
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(untagged, try_from = "u8", into = "u8"))]
#[repr(u8)]
pub enum ConnectionState {
BeforeHandshake,
Synchronizing,
Standby,
Idle,
#[default]
Normal,
}
impl ConnectionState {
/// Convert [`Self`] to a [`u8`].
///
/// ```rust
/// use cuprate_p2p_core::types::ConnectionState as C;
///
/// assert_eq!(C::BeforeHandshake.to_u8(), 0);
/// assert_eq!(C::Synchronizing.to_u8(), 1);
/// assert_eq!(C::Standby.to_u8(), 2);
/// assert_eq!(C::Idle.to_u8(), 3);
/// assert_eq!(C::Normal.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::types::ConnectionState as C;
///
/// assert_eq!(C::from_u8(0), Some(C::BeforeHandShake));
/// assert_eq!(C::from_u8(1), Some(C::Synchronizing));
/// assert_eq!(C::from_u8(2), Some(C::Standby));
/// assert_eq!(C::from_u8(3), Some(C::Idle));
/// assert_eq!(C::from_u8(4), Some(C::Normal));
/// assert_eq!(C::from_u8(5), None);
/// ```
pub const fn from_u8(u: u8) -> Option<Self> {
Some(match u {
0 => Self::BeforeHandshake,
1 => Self::Synchronizing,
2 => Self::Standby,
3 => Self::Idle,
4 => Self::Normal,
_ => return None,
})
}
}
impl From<ConnectionState> for u8 {
fn from(value: ConnectionState) -> Self {
value.to_u8()
}
}
impl TryFrom<u8> for ConnectionState {
type Error = u8;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match Self::from_u8(value) {
Some(s) => Ok(s),
None => Err(value),
}
}
}
#[cfg(feature = "epee")]
impl EpeeValue for ConnectionState {
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(())
}
}

View file

@ -135,7 +135,7 @@ define_struct_and_impl_epee! {
// Exists in the original definition, but isn't // Exists in the original definition, but isn't
// used or (de)serialized for RPC purposes. // used or (de)serialized for RPC purposes.
// ssl: bool, // ssl: bool,
state: String, state: crate::misc::ConnectionState,
support_flags: u32, support_flags: u32,
} }
} }

View file

@ -14,6 +14,7 @@
//---------------------------------------------------------------------------------------------------- Mod //---------------------------------------------------------------------------------------------------- Mod
mod address_type; mod address_type;
mod binary_string; mod binary_string;
mod connection_state;
mod distribution; mod distribution;
mod key_image_spent_status; mod key_image_spent_status;
#[expect(clippy::module_inception)] #[expect(clippy::module_inception)]
@ -24,6 +25,7 @@ mod tx_entry;
pub use address_type::AddressType; pub use address_type::AddressType;
pub use binary_string::BinaryString; pub use binary_string::BinaryString;
pub use connection_state::ConnectionState;
pub use distribution::{Distribution, DistributionCompressedBinary, DistributionUncompressed}; pub use distribution::{Distribution, DistributionCompressedBinary, DistributionUncompressed};
pub use key_image_spent_status::KeyImageSpentStatus; pub use key_image_spent_status::KeyImageSpentStatus;
pub use misc::{ pub use misc::{