ability to see basic gatekeeped network information

Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
This commit is contained in:
Uncle Stretch 2025-08-07 23:07:56 +03:00
parent 29622e6ec3
commit 9afda5a701
Signed by: str3tch
GPG Key ID: 84F3190747EE79AA
16 changed files with 517 additions and 15 deletions

View File

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

View File

@ -7,7 +7,7 @@ use subxt::config::substrate::DigestItem;
use crate::types::{ use crate::types::{
ActionLevel, ActionTarget, CasperExtrinsicDetails, EraInfo, EraRewardPoints, ActionLevel, ActionTarget, CasperExtrinsicDetails, EraInfo, EraRewardPoints,
Nominator, Nominations, PeerInformation, SessionKeyInfo, UnlockChunk, SystemAccount, Nominator, Nominations, PeerInformation, SessionKeyInfo, UnlockChunk, SystemAccount,
RewardDestination, RewardDestination, Gatekeeper, BlockRange,
}; };
#[derive(Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Display, Serialize, Deserialize)]
@ -120,9 +120,11 @@ pub enum Action {
SetGenesisHash(Option<H256>), SetGenesisHash(Option<H256>),
SetChainName(Option<String>), SetChainName(Option<String>),
SetChainVersion(Option<String>), SetChainVersion(Option<String>),
SetBlockRange(u64, BlockRange),
SetStashAccount([u8; 32]), SetStashAccount([u8; 32]),
SetStashSecret([u8; 32]), SetStashSecret([u8; 32]),
SetChoosenValidator([u8; 32], u32, u32), SetChoosenValidator([u8; 32], u32, u32),
SetChoosenGatekeeper(u64),
SetSlashingSpansLength(usize, [u8; 32]), SetSlashingSpansLength(usize, [u8; 32]),
BestBlockInformation(H256, u32), BestBlockInformation(H256, u32),
@ -153,8 +155,11 @@ pub enum Action {
GetTotalIssuance, GetTotalIssuance,
GetExistentialDeposit, GetExistentialDeposit,
GetMinValidatorBond, GetMinValidatorBond,
GetGatekeepedNetwork(u64),
GetBlockRange,
SetExistentialDeposit(u128), SetExistentialDeposit(u128),
SetMinValidatorBond(u128), SetMinValidatorBond(u128),
SetGatekeepedNetwork(Gatekeeper),
SetTotalIssuance(Option<u128>), SetTotalIssuance(Option<u128>),
} }

View File

@ -150,6 +150,7 @@ impl App {
self.network_tx.send(Action::GetPendingExtrinsics)?; self.network_tx.send(Action::GetPendingExtrinsics)?;
self.network_tx.send(Action::GetConnectedPeers)?; self.network_tx.send(Action::GetConnectedPeers)?;
self.network_tx.send(Action::CheckPendingTransactions)?; self.network_tx.send(Action::CheckPendingTransactions)?;
self.network_tx.send(Action::GetBlockRange)?;
Ok(()) Ok(())
} }

View File

@ -0,0 +1,126 @@
use std::collections::HashMap;
use color_eyre::Result;
use ratatui::layout::Constraint;
use ratatui::{
text::Text,
layout::{Alignment, Rect},
widgets::{Block, Cell, Row, Table},
Frame
};
use super::{PartialComponent, Component, CurrentTab};
use crate::types::BlockRange;
use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
#[derive(Debug, Default, Clone)]
struct Details {
incoming_fee: String,
outgoing_fee: String,
gatekeeper: String,
}
#[derive(Default)]
pub struct GatekeeperDetails {
palette: StylePalette,
gatekeeper_details: HashMap<u64, Details>,
block_ranges: HashMap<u64, BlockRange>,
selected_chain_id: u64,
}
impl PartialComponent for GatekeeperDetails {
fn set_active(&mut self, _current_tab: CurrentTab) { }
}
impl Component for GatekeeperDetails {
fn register_config_handler(&mut self, config: Config) -> Result<()> {
if let Some(style) = config.styles.get(&crate::app::Mode::Validator) {
self.palette.with_normal_style(style.get("normal_style").copied());
self.palette.with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied());
self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied());
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied());
}
Ok(())
}
fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action {
Action::SetChoosenGatekeeper(chain_id) => self.selected_chain_id = chain_id,
Action::SetBlockRange(chain_id, block_range) => {
let _ = self.block_ranges.insert(chain_id, block_range);
},
Action::SetGatekeepedNetwork(network) => {
self.gatekeeper_details.insert(network.chain_id, Details {
incoming_fee: format!("{:.5}%", network.incoming_fee as f64 / 1_000_000_000.0),
outgoing_fee: format!("{:.5}%", network.outgoing_fee as f64 / 1_000_000_000.0),
gatekeeper: network.gatekeeper,
});
}
_ => {}
};
Ok(None)
}
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
let [_, place] = super::validator_gatekeeped_networks_layout(area);
let (border_style, border_type) = self.palette.create_border_style(false);
let current_gatekeeper_details = self.gatekeeper_details
.get(&self.selected_chain_id)
.cloned()
.unwrap_or_default();
let current_block_range = self.block_ranges
.get(&self.selected_chain_id)
.copied()
.unwrap_or_default();
let table = Table::new(
vec![
Row::new(vec![
Cell::from(Text::from("From block".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(current_block_range.from_block.to_string()).alignment(Alignment::Right)),
]),
Row::new(vec![
Cell::from(Text::from("To block".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(current_block_range.to_block.to_string()).alignment(Alignment::Right)),
]),
Row::new(vec![
Cell::from(Text::from("Incoming fee".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(current_gatekeeper_details.incoming_fee).alignment(Alignment::Right)),
]),
Row::new(vec![
Cell::from(Text::from("Outgoing fee".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(current_gatekeeper_details.outgoing_fee).alignment(Alignment::Right)),
]),
Row::new(vec![
Cell::from(Text::from("Gatekeeper".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(current_gatekeeper_details.gatekeeper).alignment(Alignment::Right)),
]),
],
[
Constraint::Length(12),
Constraint::Fill(1),
],
)
.highlight_style(self.palette.create_highlight_style())
.column_spacing(1)
.block(Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false))
.title(format!("Chain ID: {}", self.selected_chain_id)));
frame.render_widget(table, place);
Ok(())
}
}

View File

@ -0,0 +1,235 @@
use std::collections::HashMap;
use color_eyre::Result;
use crossterm::event::{KeyCode, KeyEvent};
use ratatui::layout::Margin;
use ratatui::{
layout::{Alignment, Rect},
widgets::{
Block, List, ListState, ListItem, Scrollbar,
ScrollbarOrientation, ScrollbarState,
},
Frame
};
use tokio::sync::mpsc::UnboundedSender;
use super::{PartialComponent, Component, CurrentTab};
use crate::types::Gatekeeper;
use crate::{
action::Action,
config::Config,
palette::StylePalette,
};
struct NetworkDetails {
chain_id: u64,
chain_name: String,
chain_type: String,
}
pub struct Gatekeepers {
is_active: bool,
action_tx: Option<UnboundedSender<Action>>,
palette: StylePalette,
scroll_state: ScrollbarState,
list_state: ListState,
gatekeepers: Vec<NetworkDetails>,
chain_ids: HashMap<u64, usize>,
}
impl Default for Gatekeepers {
fn default() -> Self {
Self::new()
}
}
impl Gatekeepers {
pub fn new() -> Self {
Self {
is_active: false,
action_tx: None,
scroll_state: ScrollbarState::new(0),
list_state: ListState::default(),
gatekeepers: Vec::new(),
palette: StylePalette::default(),
chain_ids: Default::default(),
}
}
fn change_choosen_gatekeeper(&mut self) {
if let Some(action_tx) = &self.action_tx {
if let Some(chain_id) = self.list_state
.selected()
.map(|index| self.gatekeepers
.get(index)
.map(|data| data.chain_id)
)
.flatten()
{
let _ = action_tx.send(Action::SetChoosenGatekeeper(chain_id));
}
}
}
fn update_gatekeeped_network(&mut self, network: Gatekeeper) {
if let Some(index) = self.chain_ids.get(&network.chain_id) {
self.gatekeepers[*index] = NetworkDetails {
chain_id: network.chain_id,
chain_name: network.chain_name,
chain_type: network.chain_type,
};
} else {
let position = self.gatekeepers.len();
self.chain_ids.insert(network.chain_id, position);
self.gatekeepers.push(NetworkDetails {
chain_id: network.chain_id,
chain_name: network.chain_name,
chain_type: network.chain_type,
});
if position == 0 {
self.first_row();
}
}
}
fn first_row(&mut self) {
if self.gatekeepers.len() > 0 {
self.list_state.select(Some(0));
self.scroll_state = self.scroll_state.position(0);
}
self.change_choosen_gatekeeper();
}
fn next_row(&mut self) {
let i = match self.list_state.selected() {
Some(i) => {
if i >= self.gatekeepers.len() - 1 {
i
} else {
i + 1
}
},
None => 0,
};
self.list_state.select(Some(i));
self.scroll_state = self.scroll_state.position(i);
self.change_choosen_gatekeeper();
}
fn last_row(&mut self) {
if self.gatekeepers.len() > 0 {
let last = self.gatekeepers.len() - 1;
self.list_state.select(Some(last));
self.scroll_state = self.scroll_state.position(last);
}
self.change_choosen_gatekeeper();
}
fn previous_row(&mut self) {
let i = match self.list_state.selected() {
Some(i) => {
if i == 0 {
0
} else {
i - 1
}
},
None => 0
};
self.list_state.select(Some(i));
self.scroll_state = self.scroll_state.position(i);
self.change_choosen_gatekeeper();
}
}
impl PartialComponent for Gatekeepers {
fn set_active(&mut self, current_tab: CurrentTab) {
match current_tab {
CurrentTab::Gatekeepers => self.is_active = true,
_ => {
self.is_active = false;
self.list_state.select(None);
self.scroll_state = self.scroll_state.position(0);
}
}
}
}
impl Component for Gatekeepers {
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::Validator) {
self.palette.with_normal_style(style.get("normal_style").copied());
self.palette.with_hover_style(style.get("hover_style").copied());
self.palette.with_normal_border_style(style.get("normal_border_style").copied());
self.palette.with_hover_border_style(style.get("hover_border_style").copied());
self.palette.with_normal_title_style(style.get("normal_title_style").copied());
self.palette.with_hover_title_style(style.get("hover_title_style").copied());
self.palette.with_highlight_style(style.get("highlight_style").copied());
self.palette.with_scrollbar_style(style.get("scrollbar_style").copied());
}
Ok(())
}
fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action {
Action::SetGatekeepedNetwork(network) =>
self.update_gatekeeped_network(network),
_ => {}
};
Ok(None)
}
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> {
if self.is_active {
match key.code {
KeyCode::Up | KeyCode::Char('k') => self.previous_row(),
KeyCode::Down | KeyCode::Char('j') => self.next_row(),
KeyCode::Char('g') => self.first_row(),
KeyCode::Char('G') => self.last_row(),
_ => {},
};
}
Ok(None)
}
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
let [place, _] = super::validator_gatekeeped_networks_layout(area);
let (border_style, border_type) = self.palette.create_border_style(self.is_active);
let list = List::new(
self.gatekeepers
.iter()
.map(|network| ListItem::new(format!("{} ({}) | {}",
network.chain_name,
network.chain_id,
network.chain_type))
)
)
.highlight_style(self.palette.create_highlight_style())
.block(Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false))
.title("Gatekeeped Networks"));
let scrollbar = Scrollbar::default()
.orientation(ScrollbarOrientation::VerticalRight)
.begin_symbol(None)
.end_symbol(None)
.style(self.palette.create_scrollbar_style());
frame.render_stateful_widget(list, place, &mut self.list_state);
frame.render_stateful_widget(
scrollbar,
place.inner(Margin { vertical: 1, horizontal: 1 }),
&mut self.scroll_state,
);
Ok(())
}
}

View File

@ -150,7 +150,7 @@ impl Component for ListenAddresses {
} }
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
let [_, place] = super::validator_session_and_listen_layout(area); let [_, place, _] = super::validator_session_and_listen_layout(area);
let (border_style, border_type) = self.palette.create_border_style(self.is_active); let (border_style, border_type) = self.palette.create_border_style(self.is_active);
let list = List::new( let list = List::new(
self.listen_addresses self.listen_addresses

View File

@ -16,6 +16,8 @@ mod peers;
mod stash_info; mod stash_info;
mod nominators; mod nominators;
mod listen_addresses; mod listen_addresses;
mod gatekeepers;
mod gatekeeper_details;
mod history; mod history;
mod withdrawals; mod withdrawals;
mod stash_details; mod stash_details;
@ -39,6 +41,8 @@ use event_log::EventLogs;
use peers::Peers; use peers::Peers;
use stash_info::StashInfo; use stash_info::StashInfo;
use listen_addresses::ListenAddresses; use listen_addresses::ListenAddresses;
use gatekeepers::Gatekeepers;
use gatekeeper_details::GatekeeperDetails;
use nominators::NominatorsByValidator; use nominators::NominatorsByValidator;
use history::History; use history::History;
use withdrawals::Withdrawals; use withdrawals::Withdrawals;
@ -57,6 +61,7 @@ use payee_popup::PayeePopup;
pub enum CurrentTab { pub enum CurrentTab {
Nothing, Nothing,
ListenAddresses, ListenAddresses,
Gatekeepers,
NominatorsByValidator, NominatorsByValidator,
History, History,
Withdrawals, Withdrawals,
@ -97,10 +102,12 @@ impl Default for Validator {
Box::new(StashDetails::default()), Box::new(StashDetails::default()),
Box::new(StakingDetails::default()), Box::new(StakingDetails::default()),
Box::new(RewardDetails::default()), Box::new(RewardDetails::default()),
Box::new(GatekeeperDetails::default()),
Box::new(History::default()), Box::new(History::default()),
Box::new(Withdrawals::default()), Box::new(Withdrawals::default()),
Box::new(Peers::default()), Box::new(Peers::default()),
Box::new(ListenAddresses::default()), Box::new(ListenAddresses::default()),
Box::new(Gatekeepers::default()),
Box::new(EventLogs::default()), Box::new(EventLogs::default()),
Box::new(BondPopup::default()), Box::new(BondPopup::default()),
Box::new(PayoutPopup::default()), Box::new(PayoutPopup::default()),
@ -124,14 +131,16 @@ impl Validator {
CurrentTab::Peers => self.current_tab = CurrentTab::Withdrawals, CurrentTab::Peers => self.current_tab = CurrentTab::Withdrawals,
CurrentTab::Withdrawals => self.current_tab = CurrentTab::History, CurrentTab::Withdrawals => self.current_tab = CurrentTab::History,
CurrentTab::History => self.current_tab = CurrentTab::NominatorsByValidator, CurrentTab::History => self.current_tab = CurrentTab::NominatorsByValidator,
CurrentTab::ListenAddresses => self.current_tab = CurrentTab::NominatorsByValidator, CurrentTab::NominatorsByValidator => self.current_tab = CurrentTab::Gatekeepers,
CurrentTab::ListenAddresses => self.current_tab = CurrentTab::Gatekeepers,
_ => {} _ => {}
} }
} }
fn move_right(&mut self) { fn move_right(&mut self) {
match self.current_tab { match self.current_tab {
CurrentTab::ListenAddresses => self.current_tab = CurrentTab::NominatorsByValidator, CurrentTab::ListenAddresses => self.current_tab = CurrentTab::Gatekeepers,
CurrentTab::Gatekeepers => self.current_tab = CurrentTab::NominatorsByValidator,
CurrentTab::Nothing => self.current_tab = CurrentTab::NominatorsByValidator, CurrentTab::Nothing => self.current_tab = CurrentTab::NominatorsByValidator,
CurrentTab::NominatorsByValidator => self.current_tab = CurrentTab::History, CurrentTab::NominatorsByValidator => self.current_tab = CurrentTab::History,
CurrentTab::History => self.current_tab = CurrentTab::Withdrawals, CurrentTab::History => self.current_tab = CurrentTab::Withdrawals,
@ -278,7 +287,7 @@ impl Component for Validator {
pub fn validator_layout(area: Rect) -> [Rect; 4] { pub fn validator_layout(area: Rect) -> [Rect; 4] {
Layout::vertical([ Layout::vertical([
Constraint::Length(16), Constraint::Length(19),
Constraint::Fill(1), Constraint::Fill(1),
Constraint::Fill(1), Constraint::Fill(1),
Constraint::Percentage(25), Constraint::Percentage(25),
@ -293,14 +302,23 @@ pub fn validator_details_layout(area: Rect) -> [Rect; 2] {
]).areas(place) ]).areas(place)
} }
pub fn validator_session_and_listen_layout(area: Rect) -> [Rect; 2] { pub fn validator_session_and_listen_layout(area: Rect) -> [Rect; 3] {
let [_, place] = validator_details_layout(area); let [_, place] = validator_details_layout(area);
Layout::vertical([ Layout::vertical([
Constraint::Length(6),
Constraint::Length(6), Constraint::Length(6),
Constraint::Fill(1), Constraint::Fill(1),
]).areas(place) ]).areas(place)
} }
pub fn validator_gatekeeped_networks_layout(area: Rect) -> [Rect; 2] {
let [_, _, place] = validator_session_and_listen_layout(area);
Layout::horizontal([
Constraint::Fill(1),
Constraint::Length(57),
]).areas(place)
}
pub fn validator_statistics_layout(area: Rect) -> [Rect; 3] { pub fn validator_statistics_layout(area: Rect) -> [Rect; 3] {
let [_, place, _, _] = validator_layout(area); let [_, place, _, _] = validator_layout(area);
Layout::horizontal([ Layout::horizontal([
@ -314,7 +332,7 @@ pub fn validator_balance_layout(area: Rect) -> [Rect; 3] {
let [place, _] = validator_details_layout(area); let [place, _] = validator_details_layout(area);
Layout::vertical([ Layout::vertical([
Constraint::Length(6), Constraint::Length(6),
Constraint::Length(5), Constraint::Length(6),
Constraint::Length(5), Constraint::Length(9),
]).areas(place) ]).areas(place)
} }

View File

@ -217,7 +217,7 @@ impl Component for StashInfo {
} }
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> { fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
let [place, _] = super::validator_session_and_listen_layout(area); let [place, _, _] = super::validator_session_and_listen_layout(area);
let (border_style, border_type) = self.palette.create_border_style(self.is_active); let (border_style, border_type) = self.palette.create_border_style(self.is_active);
let table = Table::new( let table = Table::new(
self.key_names self.key_names

View File

@ -1,8 +1,9 @@
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use color_eyre::Result; use color_eyre::Result;
use subxt::{backend::{legacy::rpc_methods::SystemHealth, rpc::RpcClient}, rpc_params}; use subxt::{backend::{legacy::rpc_methods::SystemHealth, rpc::RpcClient}, rpc_params};
use codec::{Encode, Decode};
use crate::{action::Action, types::PeerInformation}; use crate::{action::Action, network::miscellaneous::get_slow_clap_storage_key, types::{BlockRange, PeerInformation}};
pub async fn get_node_name( pub async fn get_node_name(
action_tx: &UnboundedSender<Action>, action_tx: &UnboundedSender<Action>,
@ -132,3 +133,31 @@ pub async fn rotate_keys(
action_tx.send(Action::StoreRotatedKeys(rotated_keys))?; action_tx.send(Action::StoreRotatedKeys(rotated_keys))?;
Ok(()) Ok(())
} }
pub async fn get_block_range(
action_tx: &UnboundedSender<Action>,
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"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 block_range: BlockRange = 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");
let mut cursor = &bytes[..];
let from_block: u64 = u64::decode(&mut cursor).expect("first valid");
let to_block: u64 = u64::decode(&mut cursor).expect("second valid");
BlockRange { from_block, to_block }
})
.unwrap_or_default();
action_tx.send(Action::SetBlockRange(chain_id, block_range))?;
Ok(())
}

View File

@ -1,5 +1,7 @@
use subxt::ext::sp_runtime::Perbill; use subxt::ext::sp_runtime::Perbill;
const SLOW_CLAP_DB_PREFIX: &[u8] = b"slow_clap::";
// generated outside, based on params // generated outside, based on params
// MIN_INFLATION: u32 = 0_006_900; // MIN_INFLATION: u32 = 0_006_900;
// MAX_INFLATION: u32 = 0_690_000; // MAX_INFLATION: u32 = 0_690_000;
@ -112,3 +114,10 @@ pub fn prepare_perbill_fraction_string(value: Perbill) -> String {
format!("{}.{:02}%", units, rest / m) format!("{}.{:02}%", units, rest / m)
} }
pub fn get_slow_clap_storage_key(first: &[u8], second: &[u8]) -> Vec<u8> {
let mut key = SLOW_CLAP_DB_PREFIX.to_vec();
key.extend(first);
key.extend(second);
key
}

View File

@ -21,6 +21,10 @@ use crate::{
pub use subscriptions::{FinalizedSubscription, BestSubscription}; pub use subscriptions::{FinalizedSubscription, BestSubscription};
const GATEKEEPED_CHAIN_IDS: [u64; 1] = [
11155111, //Sepolia
];
struct TxToWatch { struct TxToWatch {
tx_progress: TxProgress<CasperConfig, OnlineClient<CasperConfig>>, tx_progress: TxProgress<CasperConfig, OnlineClient<CasperConfig>>,
sender: String, sender: String,
@ -186,12 +190,20 @@ impl Network {
Action::GetListenAddresses => legacy_rpc_calls::get_listen_addresses(&self.action_tx, &self.rpc_client).await, Action::GetListenAddresses => legacy_rpc_calls::get_listen_addresses(&self.action_tx, &self.rpc_client).await,
Action::GetLocalIdentity => legacy_rpc_calls::get_local_identity(&self.action_tx, &self.rpc_client).await, Action::GetLocalIdentity => legacy_rpc_calls::get_local_identity(&self.action_tx, &self.rpc_client).await,
Action::RotateSessionKeys => legacy_rpc_calls::rotate_keys(&self.action_tx, &self.rpc_client).await, Action::RotateSessionKeys => legacy_rpc_calls::rotate_keys(&self.action_tx, &self.rpc_client).await,
Action::GetBlockRange => {
for chain_id in GATEKEEPED_CHAIN_IDS {
legacy_rpc_calls::get_block_range(&self.action_tx, &self.rpc_client, chain_id).await?;
}
Ok(())
}
Action::GetBlockAuthor(hash, logs) => predefined_calls::get_block_author(&self.action_tx, &self.online_client_api, &logs, &hash).await, Action::GetBlockAuthor(hash, logs) => predefined_calls::get_block_author(&self.action_tx, &self.online_client_api, &logs, &hash).await,
Action::GetActiveEra => predefined_calls::get_active_era(&self.action_tx, &self.online_client_api).await, Action::GetActiveEra => predefined_calls::get_active_era(&self.action_tx, &self.online_client_api).await,
Action::GetCurrentEra => predefined_calls::get_current_era(&self.action_tx, &self.online_client_api).await, Action::GetCurrentEra => predefined_calls::get_current_era(&self.action_tx, &self.online_client_api).await,
Action::GetEpochProgress => predefined_calls::get_epoch_progress(&self.action_tx, &self.online_client_api).await, Action::GetEpochProgress => predefined_calls::get_epoch_progress(&self.action_tx, &self.online_client_api).await,
Action::GetMinValidatorBond => predefined_calls::get_minimal_validator_bond(&self.action_tx, &self.online_client_api).await, Action::GetMinValidatorBond => predefined_calls::get_minimal_validator_bond(&self.action_tx, &self.online_client_api).await,
Action::GetGatekeepedNetwork(chain_id) => predefined_calls::get_gatekeeped_network(&self.action_tx, &self.online_client_api, chain_id).await,
Action::GetExistentialDeposit => predefined_calls::get_existential_deposit(&self.action_tx, &self.online_client_api).await, Action::GetExistentialDeposit => predefined_calls::get_existential_deposit(&self.action_tx, &self.online_client_api).await,
Action::GetTotalIssuance => predefined_calls::get_total_issuance(&self.action_tx, &self.online_client_api).await, Action::GetTotalIssuance => predefined_calls::get_total_issuance(&self.action_tx, &self.online_client_api).await,

View File

@ -13,8 +13,8 @@ use subxt::{
use crate::{ use crate::{
action::Action, action::Action,
casper_network::runtime_types::{pallet_staking::RewardDestination, sp_consensus_slots}, casper_network::runtime_types::{ghost_networks::NetworkType, pallet_staking::RewardDestination, sp_consensus_slots},
types::{EraInfo, EraRewardPoints, Nominator, Nominations, SessionKeyInfo, SystemAccount, UnlockChunk}, types::{EraInfo, EraRewardPoints, Gatekeeper, Nominations, Nominator, SessionKeyInfo, SystemAccount, UnlockChunk},
CasperAccountId, CasperConfig CasperAccountId, CasperConfig
}; };
@ -644,3 +644,29 @@ pub async fn get_account_payee(
action_tx.send(Action::SetStakingPayee(payee, *account_id))?; action_tx.send(Action::SetStakingPayee(payee, *account_id))?;
Ok(()) Ok(())
} }
pub async fn get_gatekeeped_network(
action_tx: &UnboundedSender<Action>,
api: &OnlineClient<CasperConfig>,
chain_id: u64,
) -> Result<()> {
let gatekeeped_network = super::raw_calls::networks::networks(api, None, chain_id)
.await?
.map(|network| Gatekeeper {
chain_id,
chain_name: String::from_utf8_lossy(&network.chain_name)
.to_string(),
chain_type: match network.network_type {
NetworkType::Evm => String::from("EVM"),
NetworkType::Utxo => String::from("UTXO"),
NetworkType::Undefined => String::from("???"),
},
gatekeeper: String::from_utf8_lossy(&network.gatekeeper)
.to_string(),
incoming_fee: network.incoming_fee,
outgoing_fee: network.outgoing_fee,
})
.unwrap_or_default();
action_tx.send(Action::SetGatekeepedNetwork(gatekeeped_network))?;
Ok(())
}

View File

@ -7,7 +7,7 @@ use subxt::{
use crate::{ use crate::{
casper_network::{ casper_network::{
self, self,
runtime_types::ghost_networks::BridgeAdjustment, runtime_types::ghost_networks::{BridgeAdjustment, NetworkData},
}, },
CasperConfig, CasperConfig,
}; };
@ -29,3 +29,13 @@ pub async fn accumulated_commission(
let maybe_accumulated_commission = super::do_storage_call(online_client, &storage_key, at_hash).await?; let maybe_accumulated_commission = super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_accumulated_commission) Ok(maybe_accumulated_commission)
} }
pub async fn networks(
online_client: &OnlineClient<CasperConfig>,
at_hash: Option<&H256>,
chain_id: u64,
) -> Result<Option<NetworkData>> {
let storage_key = casper_network::storage().ghost_networks().networks(chain_id);
let maybe_network = super::do_storage_call(online_client, &storage_key, at_hash).await?;
Ok(maybe_network)
}

View File

@ -1,6 +1,12 @@
use crate::{types::CasperExtrinsicDetails, action::Action, casper::CasperBlock}; use crate::{
types::CasperExtrinsicDetails,
action::Action,
casper::CasperBlock,
};
use color_eyre::Result; use color_eyre::Result;
use super::GATEKEEPED_CHAIN_IDS;
pub struct FinalizedSubscription { pub struct FinalizedSubscription {
action_tx: tokio::sync::mpsc::UnboundedSender<Action>, action_tx: tokio::sync::mpsc::UnboundedSender<Action>,
network_tx: std::sync::mpsc::Sender<Action>, network_tx: std::sync::mpsc::Sender<Action>,
@ -120,6 +126,10 @@ impl BestSubscription {
self.network_tx.send(Action::GetInflation)?; self.network_tx.send(Action::GetInflation)?;
self.network_tx.send(Action::GetCurrentValidatorEraRewards)?; self.network_tx.send(Action::GetCurrentValidatorEraRewards)?;
self.network_tx.send(Action::GetMinValidatorBond)?; self.network_tx.send(Action::GetMinValidatorBond)?;
for chain_id in GATEKEEPED_CHAIN_IDS {
self.network_tx.send(Action::GetGatekeepedNetwork(chain_id))?;
}
} }
Ok(()) Ok(())
} }

View File

@ -6,6 +6,7 @@ mod peer;
mod session; mod session;
mod nominator; mod nominator;
mod staking; mod staking;
mod networks;
pub use extrinsics::CasperExtrinsicDetails; pub use extrinsics::CasperExtrinsicDetails;
pub use era::{EraRewardPoints, EraInfo}; pub use era::{EraRewardPoints, EraInfo};
@ -18,3 +19,5 @@ pub use nominator::Nominator;
pub use nominator::Nominations; pub use nominator::Nominations;
pub use staking::UnlockChunk; pub use staking::UnlockChunk;
pub use staking::RewardDestination; pub use staking::RewardDestination;
pub use networks::Gatekeeper;
pub use networks::BlockRange;

18
src/types/networks.rs Normal file
View File

@ -0,0 +1,18 @@
use codec::Decode;
use serde::{Serialize, Deserialize};
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Decode)]
pub struct Gatekeeper {
pub chain_id: u64,
pub chain_name: String,
pub chain_type: String,
pub gatekeeper: String,
pub incoming_fee: u32,
pub outgoing_fee: u32,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Decode)]
pub struct BlockRange {
pub from_block: u64,
pub to_block: u64,
}