Compare commits

...

2 Commits

Author SHA1 Message Date
718ae51dee
add ability to manipulate stored blocks
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2025-11-10 16:03:01 +03:00
abf2388761
avoid rustc 1.91.0 warnings
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2025-11-10 16:01:48 +03:00
9 changed files with 228 additions and 7 deletions

View File

@ -2,7 +2,7 @@
name = "ghost-eye"
authors = ["str3tch <stretch@ghostchain.io>"]
description = "Application for interacting with Casper/Ghost nodes that are exposing RPC only to the localhost"
version = "0.3.71"
version = "0.3.73"
edition = "2021"
homepage = "https://git.ghostchain.io/ghostchain"
repository = "https://git.ghostchain.io/ghostchain/ghost-eye"

View File

@ -38,6 +38,7 @@ pub enum Action {
PayoutValidatorPopup(u32),
PayoutAllValidatorPopup(Vec<u32>),
WithdrawValidatorPopup,
ChangeBlocksPopup,
BalanceRequest([u8; 32], bool),
BalanceResponse([u8; 32], Option<SystemAccount>),
@ -62,6 +63,7 @@ pub enum Action {
SetSessionKeys([u8; 32], String),
ValidateFrom([u8; 32], u32),
ChillFrom([u8; 32]),
ChangeBlocks(u64, u64),
UnbondFrom([u8; 32], u128),
RebondFrom([u8; 32], u128),
WithdrawUnbondedFrom([u8; 32], u32),

View File

@ -26,7 +26,7 @@ impl Activatable for Empty {
}
impl Empty {
fn prepare_inactive_text(&self) -> Vec<Line> {
fn prepare_inactive_text(&self) -> Vec<Line<'_>> {
vec![
Line::from(" ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢐⣤⣼⣿⣿⣿⣿⣿⣿⣷⣶⣦⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀"),
Line::from("⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣢⣾⣿⣿⣿⣿⣿⣿⣿⣿⣯⣿⣿⣿⣿⣿⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀"),
@ -61,7 +61,7 @@ impl Empty {
]
}
fn prepare_active_text(&self) -> Vec<Line> {
fn prepare_active_text(&self) -> Vec<Line<'_>> {
vec![
Line::from(" ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀"),
Line::from("⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠴⠊⠁⠀⠀⠀⠀⠀⠉⠒⠤⡀⠀⠀⠀⠀⠀"),

View File

@ -24,7 +24,7 @@ impl ExtrinsicsChart {
const BAR_WIDTH: usize = 6;
const BAR_GAP: usize = 1;
fn extrinsics_bar_chart(&self, width: u16) -> BarChart {
fn extrinsics_bar_chart(&self, width: u16) -> BarChart<'_> {
let (border_style, border_type) = self.palette.create_border_style(false);
let length = (width as usize) / (Self::BAR_WIDTH + Self::BAR_GAP);
@ -47,7 +47,7 @@ impl ExtrinsicsChart {
.bar_gap(1)
}
fn extrinsic_vertical_bar(&self, block_number: &u32, ext_len: &usize) -> Bar {
fn extrinsic_vertical_bar(&self, block_number: &u32, ext_len: &usize) -> Bar<'_> {
Bar::default()
.value(*ext_len as u64)
.label(Line::from(format!("..{}", block_number % 100)))

View File

@ -0,0 +1,157 @@
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use ratatui::{
layout::{Position, Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph},
Frame
};
use std::sync::mpsc::Sender;
use tokio::sync::mpsc::UnboundedSender;
use super::{Component, PartialComponent, CurrentTab};
use crate::{
action::Action,
config::Config,
palette::StylePalette,
types::{ActionTarget, ActionLevel},
widgets::{Input, InputRequest},
};
#[derive(Debug, Default)]
pub struct ChangeBlocksPopup {
is_active: bool,
action_tx: Option<UnboundedSender<Action>>,
network_tx: Option<Sender<Action>>,
chain_id: u64,
block_number: Input,
palette: StylePalette
}
impl ChangeBlocksPopup {
fn close_popup(&mut self) {
self.is_active = false;
if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::ClosePopup);
}
}
fn submit_new_block(&mut self) {
if let Some(network_tx) = &self.network_tx {
if let Some(action_tx) = &self.action_tx {
let _ = match self.block_number.value().parse::<u64>() {
Ok(new_block) => {
let _ = network_tx.send(Action::ChangeBlocks(self.chain_id, new_block));
action_tx.send(Action::ClosePopup)
}
Err(_) => action_tx.send(Action::EventLog(
"Incorrect block number format".to_string(),
ActionLevel::Error,
ActionTarget::ValidatorLog,
)),
};
}
}
}
fn enter_char(&mut self, new_char: char) {
let is_separator_needed = !self.block_number.value().contains('.') && new_char == '.';
if new_char.is_digit(10) || is_separator_needed {
let _ = self.block_number.handle(InputRequest::InsertChar(new_char));
}
}
fn delete_char(&mut self) {
let _ = self.block_number.handle(InputRequest::DeletePrevChar);
}
fn move_cursor_right(&mut self) {
let _ = self.block_number.handle(InputRequest::GoToNextChar);
}
fn move_cursor_left(&mut self) {
let _ = self.block_number.handle(InputRequest::GoToPrevChar);
}
}
impl PartialComponent for ChangeBlocksPopup {
fn set_active(&mut self, current_tab: CurrentTab) {
match current_tab {
CurrentTab::ChangeBlocksPopup => self.is_active = true,
_ => {
self.is_active = false;
self.block_number = Input::new(String::new());
}
}
}
}
impl Component for ChangeBlocksPopup {
fn register_network_handler(&mut self, tx: Sender<Action>) -> Result<()> {
self.network_tx = Some(tx);
Ok(())
}
fn register_action_handler(&mut self, tx: UnboundedSender<Action>) -> Result<()> {
self.action_tx = Some(tx);
Ok(())
}
fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Wallet) {
self.palette.with_normal_style(style.get("normal_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied());
self.palette.with_popup_style(style.get("popup_style").copied());
self.palette.with_popup_title_style(style.get("popup_title_style").copied());
}
Ok(())
}
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> {
if self.is_active && key.kind == KeyEventKind::Press {
match key.code {
KeyCode::Enter => self.submit_new_block(),
KeyCode::Esc => self.close_popup(),
KeyCode::Char(to_insert) => self.enter_char(to_insert),
KeyCode::Backspace => self.delete_char(),
KeyCode::Left => self.move_cursor_left(),
KeyCode::Right => self.move_cursor_right(),
_ => {},
};
}
Ok(None)
}
fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action {
Action::SetChoosenGatekeeper(chain_id) => self.chain_id = chain_id,
_ => {}
};
Ok(None)
}
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.is_active {
let (border_style, border_type) = self.palette.create_popup_style();
let input = Paragraph::new(self.block_number.value())
.block(Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right)
.title(format!("Bump blocks for #{:?}", self.chain_id)));
let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(57)]).flex(Flex::Center);
let [area] = v.areas(area);
let [area] = h.areas(area);
frame.render_widget(Clear, area);
frame.render_widget(input, area);
frame.set_cursor_position(Position::new(
area.x + self.block_number.cursor() as u16 + 1,
area.y + 1
));
}
Ok(())
}
}

View File

@ -55,6 +55,12 @@ impl Gatekeepers {
}
}
fn nullify_evm_blocks(&mut self) {
if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::ChangeBlocksPopup);
}
}
fn change_choosen_gatekeeper(&mut self) {
if let Some(action_tx) = &self.action_tx {
if let Some(chain_id) = self.list_state
@ -191,6 +197,7 @@ impl Component for Gatekeepers {
KeyCode::Down | KeyCode::Char('j') => self.next_row(),
KeyCode::Char('g') => self.first_row(),
KeyCode::Char('G') => self.last_row(),
KeyCode::Char('O') => self.nullify_evm_blocks(),
_ => {},
};
}

View File

@ -33,6 +33,7 @@ mod unbond_popup;
mod rebond_popup;
mod withdraw_popup;
mod payee_popup;
mod change_blocks_popup;
use stash_details::StashDetails;
use staking_details::StakingDetails;
@ -56,6 +57,7 @@ use unbond_popup::UnbondPopup;
use rebond_popup::RebondPopup;
use withdraw_popup::WithdrawPopup;
use payee_popup::PayeePopup;
use change_blocks_popup::ChangeBlocksPopup;
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum CurrentTab {
@ -77,6 +79,7 @@ pub enum CurrentTab {
RebondPopup,
WithdrawPopup,
PayeePopup,
ChangeBlocksPopup,
}
pub trait PartialComponent: Component {
@ -119,6 +122,7 @@ impl Default for Validator {
Box::new(RebondPopup::default()),
Box::new(WithdrawPopup::default()),
Box::new(PayeePopup::default()),
Box::new(ChangeBlocksPopup::default()),
],
}
}
@ -185,7 +189,8 @@ impl Component for Validator {
CurrentTab::RebondPopup |
CurrentTab::WithdrawPopup |
CurrentTab::PayoutPopup |
CurrentTab::PayoutAllPopup => {
CurrentTab::PayoutAllPopup |
CurrentTab::ChangeBlocksPopup => {
for component in self.components.iter_mut() {
component.handle_key_event(key)?;
}
@ -266,6 +271,10 @@ impl Component for Validator {
self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::WithdrawPopup;
},
Action::ChangeBlocksPopup => {
self.previous_tab = self.current_tab;
self.current_tab = CurrentTab::ChangeBlocksPopup;
},
Action::ClosePopup => self.current_tab = self.previous_tab,
_ => {},
}

View File

@ -3,7 +3,7 @@ use color_eyre::Result;
use subxt::{backend::{legacy::rpc_methods::SystemHealth, rpc::RpcClient}, rpc_params};
use codec::{Encode, Decode};
use crate::{action::Action, network::miscellaneous::get_slow_clap_storage_key, types::{BlockRange, PeerInformation}};
use crate::{action::Action, network::miscellaneous::get_slow_clap_storage_key, types::{ActionLevel, ActionTarget, BlockRange, PeerInformation}};
pub async fn get_node_name(
action_tx: &UnboundedSender<Action>,
@ -161,3 +161,41 @@ pub async fn get_block_range(
action_tx.send(Action::SetBlockRange(chain_id, block_range))?;
Ok(())
}
pub async fn nullify_blocks(
action_tx: &UnboundedSender<Action>,
rpc_client: &RpcClient,
chain_id: u64,
new_block: u64
) -> Result<()> {
let chain_id_encoded = chain_id.encode();
let block_range_key_raw = get_slow_clap_storage_key(b"block-", &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 zeroes = (new_block, new_block).encode();
let mut zeroes_bytes = String::from("0x");
for byte in &zeroes {
zeroes_bytes.push_str(&format!("{:02x}", byte));
}
match rpc_client
.request::<()>("offchain_localStorageSet", rpc_params!["PERSISTENT", block_range_key, zeroes_bytes])
.await
{
Ok(_) => action_tx.send(Action::EventLog(
format!("Blocks changed for chain id #{:?}", chain_id),
ActionLevel::Info,
ActionTarget::ValidatorLog,
))?,
Err(err) => action_tx.send(Action::EventLog(
format!("Block changing failed: {:?}", err),
ActionLevel::Error,
ActionTarget::ValidatorLog,
))?,
};
Ok(())
}

View File

@ -507,6 +507,14 @@ impl Network {
}
Ok(())
}
Action::ChangeBlocks(chain_id, new_block) => {
legacy_rpc_calls::nullify_blocks(
&self.action_tx,
&self.rpc_client,
chain_id,
new_block,
).await
}
_ => Ok(())
}
}