//! This module contains endpoints that are non-standard and only available on Lighthouse servers. use crate::{ ok_or_error, types::{BeaconState, Epoch, EthSpec, GenericResponse, ValidatorId}, BeaconNodeHttpClient, Error, StateId, StatusCode, }; use proto_array::core::ProtoArray; use reqwest::IntoUrl; use serde::{Deserialize, Serialize}; use ssz::Decode; pub use eth2_libp2p::{types::SyncState, PeerInfo}; /// Information returned by `peers` and `connected_peers`. // TODO: this should be deserializable.. #[derive(Debug, Clone, Serialize)] #[serde(bound = "T: EthSpec")] pub struct Peer { /// The Peer's ID pub peer_id: String, /// The PeerInfo associated with the peer. pub peer_info: PeerInfo, } /// The results of validators voting during an epoch. /// /// Provides information about the current and previous epochs. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct GlobalValidatorInclusionData { /// The total effective balance of all active validators during the _current_ epoch. pub current_epoch_active_gwei: u64, /// The total effective balance of all active validators during the _previous_ epoch. pub previous_epoch_active_gwei: u64, /// The total effective balance of all validators who attested during the _current_ epoch. pub current_epoch_attesting_gwei: u64, /// The total effective balance of all validators who attested during the _current_ epoch and /// agreed with the state about the beacon block at the first slot of the _current_ epoch. pub current_epoch_target_attesting_gwei: u64, /// The total effective balance of all validators who attested during the _previous_ epoch. pub previous_epoch_attesting_gwei: u64, /// The total effective balance of all validators who attested during the _previous_ epoch and /// agreed with the state about the beacon block at the first slot of the _previous_ epoch. pub previous_epoch_target_attesting_gwei: u64, /// The total effective balance of all validators who attested during the _previous_ epoch and /// agreed with the state about the beacon block at the time of attestation. pub previous_epoch_head_attesting_gwei: u64, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ValidatorInclusionData { /// True if the validator has been slashed, ever. pub is_slashed: bool, /// True if the validator can withdraw in the current epoch. pub is_withdrawable_in_current_epoch: bool, /// True if the validator was active in the state's _current_ epoch. pub is_active_in_current_epoch: bool, /// True if the validator was active in the state's _previous_ epoch. pub is_active_in_previous_epoch: bool, /// The validator's effective balance in the _current_ epoch. pub current_epoch_effective_balance_gwei: u64, /// True if the validator had an attestation included in the _current_ epoch. pub is_current_epoch_attester: bool, /// True if the validator's beacon block root attestation for the first slot of the _current_ /// epoch matches the block root known to the state. pub is_current_epoch_target_attester: bool, /// True if the validator had an attestation included in the _previous_ epoch. pub is_previous_epoch_attester: bool, /// True if the validator's beacon block root attestation for the first slot of the _previous_ /// epoch matches the block root known to the state. pub is_previous_epoch_target_attester: bool, /// True if the validator's beacon block root attestation in the _previous_ epoch at the /// attestation's slot (`attestation_data.slot`) matches the block root known to the state. pub is_previous_epoch_head_attester: bool, } #[cfg(target_os = "linux")] use {procinfo::pid, psutil::process::Process}; /// Reports on the health of the Lighthouse instance. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Health { /// The pid of this process. pub pid: u32, /// The number of threads used by this pid. pub pid_num_threads: i32, /// The total resident memory used by this pid. pub pid_mem_resident_set_size: u64, /// The total virtual memory used by this pid. pub pid_mem_virtual_memory_size: u64, /// Total virtual memory on the system pub sys_virt_mem_total: u64, /// Total virtual memory available for new processes. pub sys_virt_mem_available: u64, /// Total virtual memory used on the system pub sys_virt_mem_used: u64, /// Total virtual memory not used on the system pub sys_virt_mem_free: u64, /// Percentage of virtual memory used on the system pub sys_virt_mem_percent: f32, /// System load average over 1 minute. pub sys_loadavg_1: f64, /// System load average over 5 minutes. pub sys_loadavg_5: f64, /// System load average over 15 minutes. pub sys_loadavg_15: f64, } impl Health { #[cfg(not(target_os = "linux"))] pub fn observe() -> Result { Err("Health is only available on Linux".into()) } #[cfg(target_os = "linux")] pub fn observe() -> Result { let process = Process::current().map_err(|e| format!("Unable to get current process: {:?}", e))?; let process_mem = process .memory_info() .map_err(|e| format!("Unable to get process memory info: {:?}", e))?; let stat = pid::stat_self().map_err(|e| format!("Unable to get stat: {:?}", e))?; let vm = psutil::memory::virtual_memory() .map_err(|e| format!("Unable to get virtual memory: {:?}", e))?; let loadavg = psutil::host::loadavg().map_err(|e| format!("Unable to get loadavg: {:?}", e))?; Ok(Self { pid: process.pid(), pid_num_threads: stat.num_threads, pid_mem_resident_set_size: process_mem.rss(), pid_mem_virtual_memory_size: process_mem.vms(), sys_virt_mem_total: vm.total(), sys_virt_mem_available: vm.available(), sys_virt_mem_used: vm.used(), sys_virt_mem_free: vm.free(), sys_virt_mem_percent: vm.percent(), sys_loadavg_1: loadavg.one, sys_loadavg_5: loadavg.five, sys_loadavg_15: loadavg.fifteen, }) } } impl BeaconNodeHttpClient { /// Perform a HTTP GET request, returning `None` on a 404 error. async fn get_bytes_opt(&self, url: U) -> Result>, Error> { let response = self.client.get(url).send().await.map_err(Error::Reqwest)?; match ok_or_error(response).await { Ok(resp) => Ok(Some( resp.bytes() .await .map_err(Error::Reqwest)? .into_iter() .collect::>(), )), Err(err) => { if err.status() == Some(StatusCode::NOT_FOUND) { Ok(None) } else { Err(err) } } } } /// `GET lighthouse/health` pub async fn get_lighthouse_health(&self) -> Result, Error> { let mut path = self.server.clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("lighthouse") .push("health"); self.get(path).await } /// `GET lighthouse/syncing` pub async fn get_lighthouse_syncing(&self) -> Result, Error> { let mut path = self.server.clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("lighthouse") .push("syncing"); 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. */ /// `GET lighthouse/proto_array` pub async fn get_lighthouse_proto_array(&self) -> Result, Error> { let mut path = self.server.clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("lighthouse") .push("proto_array"); self.get(path).await } /// `GET lighthouse/validator_inclusion/{epoch}/global` pub async fn get_lighthouse_validator_inclusion_global( &self, epoch: Epoch, ) -> Result, Error> { let mut path = self.server.clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("lighthouse") .push("validator_inclusion") .push(&epoch.to_string()) .push("global"); self.get(path).await } /// `GET lighthouse/validator_inclusion/{epoch}/{validator_id}` pub async fn get_lighthouse_validator_inclusion( &self, epoch: Epoch, validator_id: ValidatorId, ) -> Result>, Error> { let mut path = self.server.clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("lighthouse") .push("validator_inclusion") .push(&epoch.to_string()) .push(&validator_id.to_string()); self.get(path).await } /// `GET lighthouse/beacon/states/{state_id}/ssz` pub async fn get_lighthouse_beacon_states_ssz( &self, state_id: &StateId, ) -> Result>, Error> { let mut path = self.server.clone(); path.path_segments_mut() .map_err(|()| Error::InvalidUrl(self.server.clone()))? .push("lighthouse") .push("beacon") .push("states") .push(&state_id.to_string()) .push("ssz"); self.get_bytes_opt(path) .await? .map(|bytes| BeaconState::from_ssz_bytes(&bytes).map_err(Error::InvalidSsz)) .transpose() } }