ghost-eye/src/network/mod.rs
Uncle Stretch de1732372e
nomination staking info aded to wallet tab
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2025-02-25 18:49:29 +03:00

459 lines
23 KiB
Rust

use tokio::sync::mpsc::UnboundedSender;
use color_eyre::Result;
use subxt::{
backend::rpc::RpcClient,
tx::{TxProgress, TxStatus},
utils::H256,
OnlineClient,
};
mod legacy_rpc_calls;
mod predefined_calls;
mod predefined_txs;
mod subscriptions;
mod miscellaneous;
mod raw_calls;
pub use miscellaneous::{prepare_perbill_fraction_string, calculate_for_fraction};
use crate::{
types::{ActionLevel, ActionTarget},
action::Action,
casper::CasperConfig,
};
pub use subscriptions::{FinalizedSubscription, BestSubscription};
struct TxToWatch {
tx_progress: TxProgress<CasperConfig, OnlineClient<CasperConfig>>,
sender: String,
target: ActionTarget,
}
pub struct Network {
action_tx: UnboundedSender<Action>,
online_client_api: OnlineClient<CasperConfig>,
rpc_client: RpcClient,
best_hash: Option<H256>,
finalized_hash: Option<H256>,
stash_to_watch: Option<[u8; 32]>,
validator_details_to_watch: Option<[u8; 32]>,
accounts_to_watch: std::collections::HashSet<[u8; 32]>,
transactions_to_watch: Vec<TxToWatch>,
eras_to_watch: std::collections::HashSet<u32>,
senders: std::collections::HashMap<String, u32>,
}
impl Network {
pub fn new(
action_tx: UnboundedSender<Action>,
online_client_api: OnlineClient<CasperConfig>,
rpc_client: RpcClient,
) -> Self {
Self {
action_tx,
online_client_api,
rpc_client,
best_hash: None,
finalized_hash: None,
stash_to_watch: None,
validator_details_to_watch: None,
accounts_to_watch: Default::default(),
transactions_to_watch: Default::default(),
eras_to_watch: Default::default(),
senders: Default::default(),
}
}
fn store_stash_or_validator_if_possible(&mut self, account_id: [u8; 32], is_stash: bool) {
if is_stash {
match self.stash_to_watch {
Some(stash) if stash == account_id => {},
_ => self.stash_to_watch = Some(account_id),
}
} else {
match self.validator_details_to_watch {
Some(stash) if stash == account_id => {},
_ => self.validator_details_to_watch = Some(account_id),
}
}
}
fn store_sender_nonce(&mut self, seed: &str, maybe_nonce: Option<u32>) {
if let Some(current_nonce) = maybe_nonce {
self.senders
.entry(seed.to_string())
.and_modify(|stored_nonce| {
if *stored_nonce < current_nonce {
*stored_nonce = current_nonce;
}
})
.or_insert(current_nonce);
} else {
let _ = self.senders.remove(seed);
}
}
fn remove_transaction_and_decrement_nonce(&mut self, index: usize) {
let removed = self.transactions_to_watch.remove(index);
self.senders
.get_mut(&removed.sender)
.map(|nonce| *nonce = nonce.saturating_sub(1));
}
pub async fn handle_network_event(&mut self, io_event: Action) -> Result<()> {
match io_event {
Action::NewBestHash(hash) => {
self.best_hash = Some(hash);
Ok(())
},
Action::NewFinalizedHash(hash) => {
self.finalized_hash = Some(hash);
if let Some(stash_to_watch) = self.stash_to_watch {
predefined_calls::get_session_keys(&self.action_tx, &self.online_client_api, &self.rpc_client, &stash_to_watch).await?;
predefined_calls::get_queued_session_keys(&self.action_tx, &self.online_client_api, &self.rpc_client, &stash_to_watch).await?;
predefined_calls::get_nominators_by_validator(&self.action_tx, &self.online_client_api, &stash_to_watch).await?;
predefined_calls::get_validator_prefs(&self.action_tx, &self.online_client_api, &stash_to_watch).await?;
predefined_calls::get_staking_value_ratio(&self.action_tx, &self.online_client_api, &stash_to_watch).await?;
predefined_calls::get_is_stash_bonded(&self.action_tx, &self.online_client_api, &stash_to_watch).await?;
predefined_calls::get_validators_ledger(&self.action_tx, &self.online_client_api, &stash_to_watch).await?;
predefined_calls::get_slashing_spans(&self.action_tx, &self.online_client_api, &stash_to_watch).await?;
predefined_calls::get_validator_in_session(&self.action_tx, &self.online_client_api, &stash_to_watch).await?;
for era_index in self.eras_to_watch.iter() {
predefined_calls::get_validator_staking_result(&self.action_tx, &self.online_client_api, &stash_to_watch, *era_index).await?;
}
}
if let Some(validator_details_to_watch) = self.validator_details_to_watch {
predefined_calls::get_nominators_by_validator(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
predefined_calls::get_validator_prefs(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
predefined_calls::get_staking_value_ratio(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
predefined_calls::get_validator_latest_claim(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
predefined_calls::get_validators_ledger(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
predefined_calls::get_is_stash_bonded(&self.action_tx, &self.online_client_api, &validator_details_to_watch).await?;
}
for account_id in self.accounts_to_watch.iter() {
predefined_calls::get_balance(&self.action_tx, &self.online_client_api, &account_id).await?;
}
Ok(())
},
Action::CheckPendingTransactions => {
let length = self.transactions_to_watch.len();
for i in (0..length).rev() {
let pending_tx = &mut self.transactions_to_watch[i];
let ext_hash = pending_tx.tx_progress.extrinsic_hash();
let log_target = pending_tx.target.clone();
match (*pending_tx).tx_progress.next().await {
Some(Ok(status)) => {
match status {
TxStatus::Validated => self.action_tx.send(Action::EventLog(format!("transaction {} is part of future queue", ext_hash), ActionLevel::Info, log_target))?,
TxStatus::Broadcasted { num_peers } => self.action_tx.send(Action::EventLog(format!("transaction {} has been broardcasted to {} nodes", ext_hash, num_peers), ActionLevel::Info, log_target))?,
TxStatus::NoLongerInBestBlock => self.action_tx.send(Action::EventLog(format!("transaction {} is no longer in a best block", ext_hash), ActionLevel::Warn, log_target))?,
TxStatus::InBestBlock(b) => self.action_tx.send(Action::EventLog(format!("transaction {} included in the block header {}", b.extrinsic_hash(), b.block_hash()), ActionLevel::Info, log_target))?,
TxStatus::InFinalizedBlock(b) => {
self.action_tx.send(Action::EventLog(format!("transaction {} has been finalized in block header {}", b.extrinsic_hash(), b.block_hash()), ActionLevel::Info, log_target))?;
self.transactions_to_watch.remove(i);
}
TxStatus::Error { message } => {
self.action_tx.send(Action::EventLog(format!("transaction {} error, something get wrong: {message}", ext_hash), ActionLevel::Error, log_target))?;
self.remove_transaction_and_decrement_nonce(i);
}
TxStatus::Invalid { message } => {
self.action_tx.send(Action::EventLog(format!("transaction {} invalid: {message}", ext_hash), ActionLevel::Error, log_target))?;
self.remove_transaction_and_decrement_nonce(i);
}
TxStatus::Dropped { message } => {
self.action_tx.send(Action::EventLog(format!("transaction {} was dropped: {message}", ext_hash), ActionLevel::Error, log_target))?;
self.remove_transaction_and_decrement_nonce(i);
}
}
},
_ => {
self.action_tx.send(Action::EventLog(format!("transaction {} was dropped", ext_hash), ActionLevel::Error, log_target))?;
self.remove_transaction_and_decrement_nonce(i);
}
}
}
Ok(())
},
Action::GetSystemHealth => legacy_rpc_calls::get_system_health(&self.action_tx, &self.rpc_client).await,
Action::GetNodeName => legacy_rpc_calls::get_node_name(&self.action_tx, &self.rpc_client).await,
Action::GetGenesisHash => legacy_rpc_calls::get_genesis_hash(&self.action_tx, &self.rpc_client).await,
Action::GetChainName => legacy_rpc_calls::get_chain_name(&self.action_tx, &self.rpc_client).await,
Action::GetChainVersion => legacy_rpc_calls::get_system_version(&self.action_tx, &self.rpc_client).await,
Action::GetPendingExtrinsics => legacy_rpc_calls::get_pending_extrinsics(&self.action_tx, &self.rpc_client).await,
Action::GetConnectedPeers => legacy_rpc_calls::get_connected_peers(&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::RotateSessionKeys => legacy_rpc_calls::rotate_keys(&self.action_tx, &self.rpc_client).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::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::GetMinValidatorBond => predefined_calls::get_minimal_validator_bond(&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::GetValidatorsNumber => predefined_calls::get_validators_number(&self.action_tx, &self.online_client_api).await,
Action::GetNominatorsNumber => predefined_calls::get_nominators_number(&self.action_tx, &self.online_client_api).await,
Action::GetInflation => predefined_calls::get_inflation(&self.action_tx, &self.online_client_api).await,
Action::GetCurrentValidatorEraRewards => predefined_calls::get_current_validator_reward_in_era(&self.action_tx, &self.online_client_api).await,
Action::SetSender(seed, maybe_nonce) => {
self.store_sender_nonce(&seed, maybe_nonce);
Ok(())
}
Action::RemoveEraToWatch(era_index) => {
self.eras_to_watch.remove(&era_index);
Ok(())
}
Action::GetValidatorInSession(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_validator_in_session(&self.action_tx, &self.online_client_api, &account_id).await
}
Action::GetValidatorLatestClaim(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_validator_latest_claim(&self.action_tx, &self.online_client_api, &account_id).await
}
Action::GetSlashingSpans(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_slashing_spans(&self.action_tx, &self.online_client_api, &account_id).await
}
Action::GetValidatorLedger(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_validators_ledger(&self.action_tx, &self.online_client_api, &account_id).await
}
Action::GetIsStashBonded(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_is_stash_bonded(&self.action_tx, &self.online_client_api, &account_id).await
},
Action::GetErasStakersOverview(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_staking_value_ratio(&self.action_tx, &self.online_client_api, &account_id).await
},
Action::GetValidatorPrefs(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_validator_prefs(&self.action_tx, &self.online_client_api, &account_id).await
},
Action::GetNominatorsByValidator(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_nominators_by_validator(&self.action_tx, &self.online_client_api, &account_id).await
},
Action::GetValidatorAllRewards(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_validator_staking_results(&self.action_tx, &self.online_client_api, &account_id).await
},
Action::GetQueuedSessionKeys(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_queued_session_keys(&self.action_tx, &self.online_client_api, &self.rpc_client, &account_id).await
},
Action::GetSessionKeys(account_id, is_stash) => {
self.store_stash_or_validator_if_possible(account_id, is_stash);
predefined_calls::get_session_keys(&self.action_tx, &self.online_client_api, &self.rpc_client, &account_id).await
},
Action::BalanceRequest(account_id, remove) => {
if remove {
let _ = self.accounts_to_watch.remove(&account_id);
Ok(())
} else {
let _ = self.accounts_to_watch.insert(account_id);
predefined_calls::get_balance(&self.action_tx, &self.online_client_api, &account_id).await
}
}
Action::TransferBalance(sender, receiver, amount) => {
let sender_cloned = sender.clone();
let maybe_nonce = self.senders.get_mut(&sender);
let sender: [u8; 32] = hex::decode(sender)
.expect("stored seed is valid hex string; qed")
.as_slice()
.try_into()
.expect("stored seed is valid length; qed");
if let Ok(tx_progress) = predefined_txs::transfer_balance(
&self.action_tx,
&self.online_client_api,
&sender,
&receiver,
&amount,
maybe_nonce,
).await {
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_cloned,
target: ActionTarget::WalletLog,
});
}
Ok(())
}
Action::BondValidatorExtraFrom(sender, amount) => {
let sender_str = hex::encode(sender);
let maybe_nonce = self.senders.get_mut(&sender_str);
if let Ok(tx_progress) = predefined_txs::bond_extra(
&self.action_tx,
&self.online_client_api,
&sender,
&amount,
maybe_nonce,
).await {
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
target: ActionTarget::ValidatorLog,
});
}
Ok(())
}
Action::BondValidatorFrom(sender, amount) => {
let sender_str = hex::encode(sender);
let maybe_nonce = self.senders.get_mut(&sender_str);
if let Ok(tx_progress) = predefined_txs::bond(
&self.action_tx,
&self.online_client_api,
&sender,
&amount,
maybe_nonce,
).await {
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
target: ActionTarget::ValidatorLog,
});
}
Ok(())
}
Action::PayoutStakers(sender, stash, era_index) => {
let sender_str = hex::encode(sender);
let maybe_nonce = self.senders.get_mut(&sender_str);
if let Ok(tx_progress) = predefined_txs::payout_stakers(
&self.action_tx,
&self.online_client_api,
&sender,
&stash,
era_index,
maybe_nonce,
).await {
self.eras_to_watch.insert(era_index);
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
target: ActionTarget::ValidatorLog,
});
}
Ok(())
}
Action::SetSessionKeys(sender, hashed_keys) => {
let sender_str = hex::encode(sender);
let maybe_nonce = self.senders.get_mut(&sender_str);
if let Ok(tx_progress) = predefined_txs::set_keys(
&self.action_tx,
&self.online_client_api,
&sender,
&hashed_keys,
maybe_nonce,
).await {
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
target: ActionTarget::ValidatorLog,
});
}
Ok(())
}
Action::ValidateFrom(sender, percent) => {
let sender_str = hex::encode(sender);
let maybe_nonce = self.senders.get_mut(&sender_str);
if let Ok(tx_progress) = predefined_txs::validate(
&self.action_tx,
&self.online_client_api,
&sender,
percent,
maybe_nonce,
).await {
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
target: ActionTarget::ValidatorLog,
});
}
Ok(())
}
Action::ChillFrom(sender) => {
let sender_str = hex::encode(sender);
let maybe_nonce = self.senders.get_mut(&sender_str);
if let Ok(tx_progress) = predefined_txs::chill(
&self.action_tx,
&self.online_client_api,
&sender,
maybe_nonce,
).await {
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
target: ActionTarget::ValidatorLog,
});
}
Ok(())
}
Action::UnbondFrom(sender, amount) => {
let sender_str = hex::encode(sender);
let maybe_nonce = self.senders.get_mut(&sender_str);
if let Ok(tx_progress) = predefined_txs::unbond(
&self.action_tx,
&self.online_client_api,
&sender,
&amount,
maybe_nonce,
).await {
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
target: ActionTarget::ValidatorLog,
});
}
Ok(())
}
Action::RebondFrom(sender, amount) => {
let sender_str = hex::encode(sender);
let maybe_nonce = self.senders.get_mut(&sender_str);
if let Ok(tx_progress) = predefined_txs::rebond(
&self.action_tx,
&self.online_client_api,
&sender,
&amount,
maybe_nonce,
).await {
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
target: ActionTarget::ValidatorLog,
});
}
Ok(())
}
Action::WithdrawUnbondedFrom(sender, spans) => {
let sender_str = hex::encode(sender);
let maybe_nonce = self.senders.get_mut(&sender_str);
if let Ok(tx_progress) = predefined_txs::withdraw_unbonded(
&self.action_tx,
&self.online_client_api,
&sender,
&spans,
maybe_nonce,
).await {
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
target: ActionTarget::ValidatorLog,
});
}
Ok(())
}
_ => Ok(())
}
}
}