diff --git a/Cargo.toml b/Cargo.toml index 7681f88..a1d5480 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "ghost-eye" authors = ["str3tch "] description = "Application for interacting with Casper/Ghost nodes that are exposing RPC only to the localhost" -version = "0.3.76" +version = "0.3.77" edition = "2021" homepage = "https://git.ghostchain.io/ghostchain" repository = "https://git.ghostchain.io/ghostchain/ghost-eye" diff --git a/src/action.rs b/src/action.rs index 04e9ff8..2cf111d 100644 --- a/src/action.rs +++ b/src/action.rs @@ -133,7 +133,10 @@ pub enum Action { SetChoosenGatekeeper(u64), SetSlashingSpansLength(usize, [u8; 32]), SetStoredRpcEndpoints(Vec), + SetLastUpdated(u64, u64), + SetRateLimitDelay(u64, u64), UpdateStoredRpcEndpoints(u64, Vec), + NullifyLastTimestamp(u64), BestBlockInformation(H256, u32), FinalizedBlockInformation(H256, u32), diff --git a/src/components/validator/gatekeeper_endpoints_popup.rs b/src/components/validator/gatekeeper_endpoints_popup.rs index 78654a2..7d2d1d5 100644 --- a/src/components/validator/gatekeeper_endpoints_popup.rs +++ b/src/components/validator/gatekeeper_endpoints_popup.rs @@ -1,14 +1,10 @@ use color_eyre::Result; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use ratatui::{ - layout::{Alignment, Constraint, Flex, Layout, Position, Rect}, - prelude::Margin, - style::{Color, Style}, - widgets::{ + layout::{Alignment, Constraint, Flex, Layout, Position, Rect}, prelude::Margin, style::{Color, Style}, text::Line, widgets::{ Block, BorderType, Cell, Clear, Paragraph, Row, Scrollbar, ScrollbarOrientation, ScrollbarState, Table, TableState, - }, - Frame, + }, Frame }; use std::sync::mpsc::Sender; use tokio::sync::mpsc::UnboundedSender; @@ -33,6 +29,8 @@ enum Selected { pub struct GatekeeperEndpoints { is_active: bool, selected: Selected, + rate_limit_delay: u64, + last_updated: u64, chain_id: u64, rpc_input: Input, action_tx: Option>, @@ -92,6 +90,11 @@ impl GatekeeperEndpoints { } } } + Selected::StoredRpcs if new_char == 'T' => { + if let Some(network_tx) = &self.network_tx { + let _ = network_tx.send(Action::NullifyLastTimestamp(self.chain_id)); + } + } _ => match new_char { 'j' => self.move_cursor_down(), 'k' => self.move_cursor_up(), @@ -233,6 +236,34 @@ impl GatekeeperEndpoints { (table, scrollbar) } + + fn prepare_rate_limits(&self) -> String { + format!("Rate limit: {}", self.rate_limit_delay / 1_000) + } + + fn prepare_datetime_string(&self) -> String { + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .expect("Time went backwards") + .as_millis(); + + let diff = now as i64 - self.last_updated as i64; + let abs_diff = diff.abs(); + + if abs_diff < 60_000 { + let secs = abs_diff / 1_000; + format!("{}secs {}", secs, if diff >= 0 { "ago" } else { "left" }) + } else if abs_diff < 3_600_000 { + let mins = abs_diff / 60_000; + format!("{}mins {}", mins, if diff >= 0 { "ago" } else { "left" }) + } else if abs_diff < 86_400_000 { + let hours = abs_diff / 3_600_000; + format!("{}hour {}", hours, if diff >= 0 { "ago" } else { "left" }) + } else { + let days = abs_diff / 86_400_000; + format!("{}days {}", days, if diff >= 0 { "ago" } else { "left" }) + } + } } impl PartialComponent for GatekeeperEndpoints { @@ -304,6 +335,16 @@ impl Component for GatekeeperEndpoints { fn update(&mut self, action: Action) -> Result> { match action { Action::SetChoosenGatekeeper(chain_id) => self.set_chain_id(chain_id), + Action::SetLastUpdated(chain_id, last_updated) => { + if chain_id == self.chain_id { + self.last_updated = last_updated; + } + } + Action::SetRateLimitDelay(chain_id, rate_limit_delay) => { + if chain_id == self.chain_id { + self.rate_limit_delay = rate_limit_delay; + } + } Action::SetGatekeepedNetwork(network) => { self.default_endpoints = network.default_endpoints } @@ -358,7 +399,9 @@ impl Component for GatekeeperEndpoints { .border_type(input_border_type) .title_style(self.palette.create_popup_title_style()) .title_alignment(Alignment::Right) - .title("Input new RPC"), + .title("Input new RPC") + .title_bottom(Line::from(self.prepare_datetime_string()).left_aligned()) + .title_bottom(Line::from(self.prepare_rate_limits()).right_aligned()), ); let v = Layout::vertical([Constraint::Max(14)]).flex(Flex::Center); diff --git a/src/components/validator/mod.rs b/src/components/validator/mod.rs index 024df70..2602ff0 100644 --- a/src/components/validator/mod.rs +++ b/src/components/validator/mod.rs @@ -191,6 +191,7 @@ impl Component for Validator { | CurrentTab::RotatePopup | CurrentTab::ValidatePopup | CurrentTab::ChillPopup + | CurrentTab::PayeePopup | CurrentTab::UnbondPopup | CurrentTab::RebondPopup | CurrentTab::WithdrawPopup diff --git a/src/network/legacy_rpc_calls.rs b/src/network/legacy_rpc_calls.rs index 7f640d1..14ee7d3 100644 --- a/src/network/legacy_rpc_calls.rs +++ b/src/network/legacy_rpc_calls.rs @@ -169,6 +169,66 @@ pub async fn get_block_range( Ok(()) } +pub async fn get_last_updated( + action_tx: &UnboundedSender, + rpc_client: &RpcClient, + chain_id: u64, +) -> Result<()> { + let chain_id_encoded = chain_id.encode(); + let block_range_key_raw = get_slow_clap_storage_key(b"last-timestamp-", &chain_id_encoded); + let mut block_range_key = String::from("0x"); + for byte in block_range_key_raw { + block_range_key.push_str(&format!("{:02x}", byte)); + } + let last_timestamp: u64 = rpc_client + .request( + "offchain_localStorageGet", + rpc_params!["PERSISTENT", block_range_key], + ) + .await + .ok() + .map(|hex_string: String| { + let bytes = hex::decode(&hex_string[2..]).expect("Invalid hex string"); + u64::decode(&mut bytes.as_slice()) + .ok() + .unwrap_or_default() + }) + .unwrap_or_default(); + + action_tx.send(Action::SetLastUpdated(chain_id, last_timestamp))?; + Ok(()) +} + +pub async fn get_rate_limit_delay( + action_tx: &UnboundedSender, + rpc_client: &RpcClient, + chain_id: u64, +) -> Result<()> { + let chain_id_encoded = chain_id.encode(); + let block_range_key_raw = get_slow_clap_storage_key(b"rate-limit-", &chain_id_encoded); + let mut block_range_key = String::from("0x"); + for byte in block_range_key_raw { + block_range_key.push_str(&format!("{:02x}", byte)); + } + let last_timestamp: u64 = rpc_client + .request( + "offchain_localStorageGet", + rpc_params!["PERSISTENT", block_range_key], + ) + .await + .ok() + .map(|hex_string: String| { + let bytes = hex::decode(&hex_string[2..]).expect("Invalid hex string"); + u64::decode(&mut bytes.as_slice()) + .ok() + .unwrap_or(5_000u64) + }) + .unwrap_or(5_000u64); + + action_tx.send(Action::SetRateLimitDelay(chain_id, last_timestamp))?; + Ok(()) +} + pub async fn nullify_blocks( action_tx: &UnboundedSender, rpc_client: &RpcClient, @@ -286,3 +346,40 @@ pub async fn set_stored_rpc_endpoints( Ok(()) } + +pub async fn set_last_timestamp( + action_tx: &UnboundedSender, + rpc_client: &RpcClient, + chain_id: u64, +) -> Result<()> { + let chain_id_encoded = chain_id.encode(); + let endpoint_key_raw = get_slow_clap_storage_key(b"last-timestamp-", &chain_id_encoded); + let mut endpoint_key = String::from("0x"); + for byte in endpoint_key_raw { + endpoint_key.push_str(&format!("{:02x}", byte)); + } + + let encoded_zero = String::from("0x0000000000000000"); + match rpc_client + .request::<()>( + "offchain_localStorageSet", + rpc_params!["PERSISTENT", endpoint_key, encoded_zero], + ) + .await + { + Ok(_) => { + action_tx.send(Action::EventLog( + format!("Last timestamp nullified for network #{:?}", chain_id), + ActionLevel::Info, + ActionTarget::ValidatorLog, + ))?; + get_stored_rpc_endpoints(action_tx, rpc_client, chain_id).await?; + } + Err(err) => action_tx.send(Action::EventLog( + format!("Last timestamp nullification failed: {:?}", err), + ActionLevel::Error, + ActionTarget::ValidatorLog, + ))?, + }; + Ok(()) +} diff --git a/src/network/mod.rs b/src/network/mod.rs index 986907a..1fa9184 100644 --- a/src/network/mod.rs +++ b/src/network/mod.rs @@ -378,6 +378,10 @@ impl Network { for chain_id in GATEKEEPED_CHAIN_IDS { legacy_rpc_calls::get_block_range(&self.action_tx, &self.rpc_client, chain_id) .await?; + legacy_rpc_calls::get_last_updated(&self.action_tx, &self.rpc_client, chain_id) + .await?; + legacy_rpc_calls::get_rate_limit_delay(&self.action_tx, &self.rpc_client, chain_id) + .await?; } Ok(()) } @@ -846,6 +850,14 @@ impl Network { ) .await } + Action::NullifyLastTimestamp(chain_id) => { + legacy_rpc_calls::set_last_timestamp( + &self.action_tx, + &self.rpc_client, + chain_id, + ) + .await + } _ => Ok(()), } }