diff --git a/Cargo.lock b/Cargo.lock index 9c1b591349b..8b508baa92f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4529,6 +4529,7 @@ dependencies = [ "rand", "regex", "serde", + "serde_utils", "sha2 0.9.9", "slog", "slog-async", @@ -6884,6 +6885,13 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_utils" +version = "0.1.0" +dependencies = [ + "serde", +] + [[package]] name = "serde_with" version = "1.14.0" diff --git a/Cargo.toml b/Cargo.toml index 9adb913ff5e..c52e88e10b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ members = [ "common/oneshot_broadcast", "common/pretty_reqwest_error", "common/sensitive_url", + "common/serde_utils", "common/slot_clock", "common/system_health", "common/task_executor", diff --git a/beacon_node/lighthouse_network/Cargo.toml b/beacon_node/lighthouse_network/Cargo.toml index 125bbe9bc2f..6c7059a4359 100644 --- a/beacon_node/lighthouse_network/Cargo.toml +++ b/beacon_node/lighthouse_network/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Sigma Prime "] edition = { workspace = true } [dependencies] +serde_utils = { path = "../../common/serde_utils" } discv5 = { workspace = true } unsigned-varint = { version = "0.6", features = ["codec"] } ssz_types = { workspace = true } diff --git a/beacon_node/lighthouse_network/src/peer_manager/peerdb/client.rs b/beacon_node/lighthouse_network/src/peer_manager/peerdb/client.rs index 1178dbcb9ce..a557c64b202 100644 --- a/beacon_node/lighthouse_network/src/peer_manager/peerdb/client.rs +++ b/beacon_node/lighthouse_network/src/peer_manager/peerdb/client.rs @@ -3,11 +3,11 @@ //! Currently using identify to fingerprint. use libp2p::identify::Info as IdentifyInfo; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use strum::{AsRefStr, EnumIter, IntoStaticStr}; /// Various client and protocol information related to a node. -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct Client { /// The client's name (Ex: lighthouse, prism, nimbus, etc) pub kind: ClientKind, @@ -21,7 +21,9 @@ pub struct Client { pub agent_string: Option, } -#[derive(Clone, Copy, Debug, Serialize, PartialEq, AsRefStr, IntoStaticStr, EnumIter)] +#[derive( + Clone, Copy, Debug, Deserialize, Serialize, PartialEq, AsRefStr, IntoStaticStr, EnumIter, +)] pub enum ClientKind { /// A lighthouse node (the best kind). Lighthouse, diff --git a/beacon_node/lighthouse_network/src/peer_manager/peerdb/peer_info.rs b/beacon_node/lighthouse_network/src/peer_manager/peerdb/peer_info.rs index 44c54511ddc..73a00364b4f 100644 --- a/beacon_node/lighthouse_network/src/peer_manager/peerdb/peer_info.rs +++ b/beacon_node/lighthouse_network/src/peer_manager/peerdb/peer_info.rs @@ -5,10 +5,7 @@ use crate::discovery::Eth2Enr; use crate::{rpc::MetaData, types::Subnet}; use discv5::Enr; use libp2p::core::multiaddr::{Multiaddr, Protocol}; -use serde::{ - ser::{SerializeStruct, Serializer}, - Serialize, -}; +use serde::{Deserialize, Serialize}; use std::collections::HashSet; use std::net::IpAddr; use std::time::Instant; @@ -16,8 +13,10 @@ use strum::AsRefStr; use types::EthSpec; use PeerConnectionStatus::*; +use serde_utils::approx_instant; + /// Information about a given connected peer. -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] #[serde(bound = "T: EthSpec")] pub struct PeerInfo { /// The peers reputation @@ -35,6 +34,7 @@ pub struct PeerInfo { /// The current syncing state of the peer. The state may be determined after it's initial /// connection. sync_status: SyncStatus, + #[serde(skip)] /// The ENR subnet bitfield of the peer. This may be determined after it's initial /// connection. meta_data: Option>, @@ -473,7 +473,7 @@ impl PeerInfo { } /// Connection Direction of connection. -#[derive(Debug, Clone, Serialize, AsRefStr)] +#[derive(Debug, Clone, Deserialize, Serialize, AsRefStr)] #[strum(serialize_all = "snake_case")] pub enum ConnectionDirection { /// The connection was established by a peer dialing us. @@ -483,7 +483,8 @@ pub enum ConnectionDirection { } /// Connection Status of the peer. -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, Deserialize, Serialize)] +#[serde(tag = "status")] pub enum PeerConnectionStatus { /// The peer is connected. Connected { @@ -499,16 +500,19 @@ pub enum PeerConnectionStatus { }, /// The peer has disconnected. Disconnected { + #[serde(with = "approx_instant")] /// last time the peer was connected or discovered. since: Instant, }, /// The peer has been banned and is disconnected. Banned { + #[serde(with = "approx_instant")] /// moment when the peer was banned. since: Instant, }, /// We are currently dialing this peer. Dialing { + #[serde(with = "approx_instant")] /// time since we last communicated with the peer. since: Instant, }, @@ -516,55 +520,3 @@ pub enum PeerConnectionStatus { #[default] Unknown, } - -/// Serialization for http requests. -impl Serialize for PeerConnectionStatus { - fn serialize(&self, serializer: S) -> Result { - let mut s = serializer.serialize_struct("connection_status", 6)?; - match self { - Connected { n_in, n_out } => { - s.serialize_field("status", "connected")?; - s.serialize_field("connections_in", n_in)?; - s.serialize_field("connections_out", n_out)?; - s.serialize_field("last_seen", &0)?; - s.end() - } - Disconnecting { .. } => { - s.serialize_field("status", "disconnecting")?; - s.serialize_field("connections_in", &0)?; - s.serialize_field("connections_out", &0)?; - s.serialize_field("last_seen", &0)?; - s.end() - } - Disconnected { since } => { - s.serialize_field("status", "disconnected")?; - s.serialize_field("connections_in", &0)?; - s.serialize_field("connections_out", &0)?; - s.serialize_field("last_seen", &since.elapsed().as_secs())?; - s.serialize_field("banned_ips", &Vec::::new())?; - s.end() - } - Banned { since } => { - s.serialize_field("status", "banned")?; - s.serialize_field("connections_in", &0)?; - s.serialize_field("connections_out", &0)?; - s.serialize_field("last_seen", &since.elapsed().as_secs())?; - s.end() - } - Dialing { since } => { - s.serialize_field("status", "dialing")?; - s.serialize_field("connections_in", &0)?; - s.serialize_field("connections_out", &0)?; - s.serialize_field("last_seen", &since.elapsed().as_secs())?; - s.end() - } - Unknown => { - s.serialize_field("status", "unknown")?; - s.serialize_field("connections_in", &0)?; - s.serialize_field("connections_out", &0)?; - s.serialize_field("last_seen", &0)?; - s.end() - } - } - } -} diff --git a/beacon_node/lighthouse_network/src/peer_manager/peerdb/score.rs b/beacon_node/lighthouse_network/src/peer_manager/peerdb/score.rs index 877d725812c..2d77b228ccf 100644 --- a/beacon_node/lighthouse_network/src/peer_manager/peerdb/score.rs +++ b/beacon_node/lighthouse_network/src/peer_manager/peerdb/score.rs @@ -6,11 +6,13 @@ //! //! The scoring algorithms are currently experimental. use crate::service::gossipsub_scoring_parameters::GREYLIST_THRESHOLD as GOSSIPSUB_GREYLIST_THRESHOLD; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use std::time::Instant; use strum::AsRefStr; use tokio::time::Duration; +use serde_utils::approx_instant; + lazy_static! { static ref HALFLIFE_DECAY: f64 = -(2.0f64.ln()) / SCORE_HALFLIFE; } @@ -124,7 +126,7 @@ impl std::fmt::Display for ScoreState { /// /// This simplistic version consists of a global score per peer which decays to 0 over time. The /// decay rate applies equally to positive and negative scores. -#[derive(PartialEq, Clone, Debug, Serialize)] +#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)] pub struct RealScore { /// The global score. // NOTE: In the future we may separate this into sub-scores involving the RPC, Gossipsub and @@ -136,7 +138,7 @@ pub struct RealScore { ignore_negative_gossipsub_score: bool, score: f64, /// The time the score was last updated to perform time-based adjustments such as score-decay. - #[serde(skip)] + #[serde(with = "approx_instant")] last_updated: Instant, } @@ -261,7 +263,7 @@ impl RealScore { } } -#[derive(PartialEq, Clone, Debug, Serialize)] +#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)] pub enum Score { Max, Real(RealScore), diff --git a/beacon_node/lighthouse_network/src/peer_manager/peerdb/sync_status.rs b/beacon_node/lighthouse_network/src/peer_manager/peerdb/sync_status.rs index bab8aa9aeb8..a0a4e35eefa 100644 --- a/beacon_node/lighthouse_network/src/peer_manager/peerdb/sync_status.rs +++ b/beacon_node/lighthouse_network/src/peer_manager/peerdb/sync_status.rs @@ -1,9 +1,9 @@ //! Handles individual sync status for peers. -use serde::Serialize; +use serde::{Deserialize, Serialize}; use types::{Epoch, Hash256, Slot}; -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] /// The current sync status of the peer. pub enum SyncStatus { /// At the current state as our node or ahead of us. @@ -19,7 +19,7 @@ pub enum SyncStatus { } /// A relevant peer's sync information. -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct SyncInfo { pub head_slot: Slot, pub head_root: Hash256, diff --git a/beacon_node/lighthouse_network/src/types/subnet.rs b/beacon_node/lighthouse_network/src/types/subnet.rs index 50d28542bec..2d7204bb2ed 100644 --- a/beacon_node/lighthouse_network/src/types/subnet.rs +++ b/beacon_node/lighthouse_network/src/types/subnet.rs @@ -1,4 +1,4 @@ -use serde::Serialize; +use serde::{Deserialize, Serialize}; use std::time::Instant; use types::{SubnetId, SyncSubnetId}; @@ -6,7 +6,7 @@ use types::{SubnetId, SyncSubnetId}; /// /// Used for subscribing to the appropriate gossipsub subnets and mark /// appropriate metadata bitfields. -#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Hash)] pub enum Subnet { /// Represents a gossipsub attestation subnet and the metadata `attnets` field. Attestation(SubnetId), diff --git a/common/eth2/src/lighthouse.rs b/common/eth2/src/lighthouse.rs index cd405386b83..0f8a98da33e 100644 --- a/common/eth2/src/lighthouse.rs +++ b/common/eth2/src/lighthouse.rs @@ -41,7 +41,7 @@ four_byte_option_impl!(four_byte_option_hash256, Hash256); /// Information returned by `peers` and `connected_peers`. // TODO: this should be deserializable.. -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone, Deserialize, Serialize)] #[serde(bound = "T: EthSpec")] pub struct Peer { /// The Peer's ID @@ -413,13 +413,16 @@ impl BeaconNodeHttpClient { self.get(path).await } - /* - * Note: - * - * The `lighthouse/peers` endpoints do not have functions here. We are yet to implement - * `Deserialize` on the `PeerInfo` struct since it contains use of `Instant`. This could be - * fairly simply achieved, if desired. - */ + pub async fn get_lighthouse_peers(&self) -> Result>, Error> { + let mut path = self.server.full.clone(); + + path.path_segments_mut() + .map_err(|()| Error::InvalidUrl(self.server.clone()))? + .push("lighthouse") + .push("peers"); + + self.get(path).await + } /// `GET lighthouse/proto_array` pub async fn get_lighthouse_proto_array(&self) -> Result, Error> { diff --git a/common/serde_utils/Cargo.toml b/common/serde_utils/Cargo.toml new file mode 100644 index 00000000000..1b2615c972f --- /dev/null +++ b/common/serde_utils/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "serde_utils" +version = "0.1.0" +authors = ["Jack McPherson "] +edition = { workspace = true } + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = { workspace = true } diff --git a/common/serde_utils/src/approx_instant.rs b/common/serde_utils/src/approx_instant.rs new file mode 100644 index 00000000000..ff59e5ab623 --- /dev/null +++ b/common/serde_utils/src/approx_instant.rs @@ -0,0 +1,27 @@ +use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; +/// Serialise and deserialise `std::time::Instant`s +/// +/// Due to David Tolnay via: https://github.com/serde-rs/serde/issues/1375#issuecomment-419688068 +use std::time::{Instant, SystemTime}; + +pub fn serialize(instant: &Instant, serializer: S) -> Result +where + S: Serializer, +{ + let system_now = SystemTime::now(); + let instant_now = Instant::now(); + let approx = system_now - (instant_now - *instant); + approx.serialize(serializer) +} + +pub fn deserialize<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let de = SystemTime::deserialize(deserializer)?; + let system_now = SystemTime::now(); + let instant_now = Instant::now(); + let duration = system_now.duration_since(de).map_err(Error::custom)?; + let approx = instant_now - duration; + Ok(approx) +} diff --git a/common/serde_utils/src/lib.rs b/common/serde_utils/src/lib.rs new file mode 100644 index 00000000000..46a2e3c5e85 --- /dev/null +++ b/common/serde_utils/src/lib.rs @@ -0,0 +1 @@ +pub mod approx_instant;