ghost-eye/src/network/mod.rs
Uncle Stretch 507c03959d
decrement stored nonce if tx failed for any reason
Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
2025-01-24 16:00:36 +03:00

252 lines
13 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]>,
accounts_to_watch: std::collections::HashSet<[u8; 32]>,
transactions_to_watch: Vec<TxToWatch>,
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,
accounts_to_watch: Default::default(),
transactions_to_watch: Default::default(),
senders: Default::default(),
}
}
fn store_stash_if_possible(&mut self, new_stash: [u8; 32]) {
match self.stash_to_watch {
Some(stash) if stash == new_stash => {},
_ => self.stash_to_watch = Some(new_stash),
}
}
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?;
}
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::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::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::GetValidatorLedger(stash) => {
self.store_stash_if_possible(stash);
predefined_calls::get_validators_ledger(&self.action_tx, &self.online_client_api, &stash).await
}
Action::GetIsStashBonded(stash) => {
self.store_stash_if_possible(stash);
predefined_calls::get_is_stash_bonded(&self.action_tx, &self.online_client_api, &stash).await
},
Action::GetErasStakersOverview(stash) => {
self.store_stash_if_possible(stash);
predefined_calls::get_staking_value_ratio(&self.action_tx, &self.online_client_api, &stash).await
},
Action::GetValidatorPrefs(stash) => {
self.store_stash_if_possible(stash);
predefined_calls::get_validator_prefs(&self.action_tx, &self.online_client_api, &stash).await
},
Action::GetValidatorAllRewards(stash) => {
self.store_stash_if_possible(stash);
predefined_calls::get_validator_staking_results(&self.action_tx, &self.online_client_api, &stash).await
},
Action::GetNominatorsByValidator(stash) => {
self.store_stash_if_possible(stash);
predefined_calls::get_nominators_by_validator(&self.action_tx, &self.online_client_api, &stash).await
},
Action::GetQueuedSessionKeys(stash) => {
self.store_stash_if_possible(stash);
predefined_calls::get_queued_session_keys(&self.action_tx, &self.online_client_api, &self.rpc_client, &stash).await
},
Action::GetSessionKeys(stash) => {
self.store_stash_if_possible(stash);
predefined_calls::get_session_keys(&self.action_tx, &self.online_client_api, &self.rpc_client, &stash).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(())
}
_ => Ok(())
}
}
}