524 lines
17 KiB
Rust
524 lines
17 KiB
Rust
use tokio::sync::mpsc::UnboundedSender;
|
|
use color_eyre::Result;
|
|
use subxt::{
|
|
backend::rpc::RpcClient,
|
|
client::OnlineClient,
|
|
config::substrate::DigestItem,
|
|
ext::sp_core::crypto::{
|
|
AccountId32, Ss58AddressFormat, Ss58Codec,
|
|
},
|
|
rpc_params,
|
|
utils::H256,
|
|
};
|
|
|
|
use crate::{
|
|
action::Action,
|
|
casper_network::runtime_types::sp_consensus_slots,
|
|
types::{EraInfo, EraRewardPoints, Nominator, SessionKeyInfo, SystemAccount},
|
|
CasperAccountId, CasperConfig
|
|
};
|
|
|
|
pub async fn get_block_author(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
logs: &Vec<DigestItem>,
|
|
at_hash: &H256,
|
|
) -> Result<()> {
|
|
use codec::Decode;
|
|
use crate::casper_network::runtime_types::sp_consensus_babe::digests::PreDigest;
|
|
|
|
let validators = super::raw_calls::session::validators(api, Some(at_hash))
|
|
.await?
|
|
.unwrap_or_default();
|
|
|
|
let maybe_author = match logs.iter().find(|item| matches!(item, DigestItem::PreRuntime(..))) {
|
|
Some(DigestItem::PreRuntime(engine, data)) if *engine == [b'B', b'A', b'B', b'E'] => {
|
|
match PreDigest::decode(&mut &data[..]) {
|
|
Ok(PreDigest::Primary(primary)) => validators.get(primary.authority_index as usize),
|
|
Ok(PreDigest::SecondaryPlain(secondary)) => validators.get(secondary.authority_index as usize),
|
|
Ok(PreDigest::SecondaryVRF(secondary)) => validators.get(secondary.authority_index as usize),
|
|
_ => None,
|
|
}
|
|
},
|
|
_ => None,
|
|
};
|
|
|
|
let validator = match maybe_author {
|
|
Some(author) => {
|
|
let extended_author = CasperAccountId::decode(&mut author.as_ref())
|
|
.expect("author should be valid AccountId32; qed");
|
|
let account_id = AccountId32::from(extended_author.0);
|
|
account_id.to_ss58check_with_version(Ss58AddressFormat::custom(1996))
|
|
},
|
|
None => "...".to_string(),
|
|
};
|
|
|
|
action_tx.send(Action::SetBlockAuthor(*at_hash, validator))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_current_era(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
) -> Result<()> {
|
|
let current_era = super::raw_calls::staking::current_era(api, None)
|
|
.await?
|
|
.unwrap_or_default();
|
|
action_tx.send(Action::SetCurrentEra(current_era))?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_active_era(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
) -> Result<()> {
|
|
if let Some(active_era) = super::raw_calls::staking::active_era(api, None).await? {
|
|
action_tx.send(Action::SetActiveEra(EraInfo {
|
|
index: active_era.index,
|
|
start: active_era.start,
|
|
}))?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_epoch_progress(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
) -> Result<()> {
|
|
let current_slot = super::raw_calls::babe::current_slot(api, None)
|
|
.await?
|
|
.unwrap_or(sp_consensus_slots::Slot(0u64));
|
|
|
|
let epoch_index = super::raw_calls::babe::epoch_index(api, None)
|
|
.await?
|
|
.unwrap_or_default();
|
|
|
|
let genesis_slot = super::raw_calls::babe::genesis_slot(api, None)
|
|
.await?
|
|
.unwrap_or(sp_consensus_slots::Slot(0u64));
|
|
|
|
let epoch_duration = super::raw_calls::babe::epoch_duration(api)?;
|
|
|
|
let epoch_start_slot = epoch_index * epoch_duration + genesis_slot.0;
|
|
let progress = current_slot.0.saturating_sub(epoch_start_slot);
|
|
|
|
action_tx.send(Action::SetEpochProgress(epoch_index, progress))?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_total_issuance(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
) -> Result<()> {
|
|
let maybe_total_issuance = super::raw_calls::balances::total_issuance(api, None)
|
|
.await?;
|
|
action_tx.send(Action::SetTotalIssuance(maybe_total_issuance))?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_existential_deposit(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
) -> Result<()> {
|
|
let existential_deposit = super::raw_calls::balances::existential_deposit(api)?;
|
|
action_tx.send(Action::SetExistentialDeposit(existential_deposit))?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_balance(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
account_id: &[u8; 32],
|
|
) -> Result<()> {
|
|
let maybe_balance = super::raw_calls::system::balance(api, None, account_id)
|
|
.await?
|
|
.map(|balance| SystemAccount {
|
|
nonce: balance.nonce,
|
|
free: balance.data.free,
|
|
reserved: balance.data.reserved,
|
|
frozen: balance.data.frozen,
|
|
}
|
|
);
|
|
action_tx.send(Action::BalanceResponse(*account_id, maybe_balance))?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_validators_number(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
) -> Result<()> {
|
|
let counter_for_validators = super::raw_calls::staking::counter_for_validators(api, None)
|
|
.await?
|
|
.unwrap_or_default();
|
|
action_tx.send(Action::ValidatorsNumber(counter_for_validators))?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_nominators_number(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
) -> Result<()> {
|
|
let counter_for_nominators = super::raw_calls::staking::counter_for_nominators(api, None)
|
|
.await?
|
|
.unwrap_or_default();
|
|
action_tx.send(Action::NominatorsNumber(counter_for_nominators))?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_inflation(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
) -> Result<()> {
|
|
let total_issuance = super::raw_calls::balances::total_issuance(api, None)
|
|
.await?
|
|
.unwrap_or_default();
|
|
|
|
let active_era_index = super::raw_calls::staking::active_era(api, None)
|
|
.await?
|
|
.map(|era_info| era_info.index)
|
|
.unwrap_or_default();
|
|
|
|
let total_staked = super::raw_calls::staking::eras_total_stake(api, None, active_era_index)
|
|
.await?
|
|
.unwrap_or_default();
|
|
|
|
let (inflation, fraction) = super::calculate_for_fraction(total_staked, total_issuance);
|
|
let inflation_str = super::prepare_perbill_fraction_string(inflation);
|
|
let fraction_str = super::prepare_perbill_fraction_string(fraction);
|
|
|
|
action_tx.send(Action::Inflation(inflation_str))?;
|
|
action_tx.send(Action::Apy(fraction_str))?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_session_keys(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
rpc_client: &RpcClient,
|
|
account_id: &[u8; 32],
|
|
) -> Result<()> {
|
|
let maybe_session_keys = super::raw_calls::session::next_keys(api, None, account_id).await?;
|
|
let (gran_key, babe_key, audi_key, slow_key) = match maybe_session_keys {
|
|
Some(session_keys) => {
|
|
let gran_key = format!("0x{}", hex::encode(session_keys.grandpa.0));
|
|
let babe_key = format!("0x{}", hex::encode(session_keys.babe.0));
|
|
let audi_key = format!("0x{}", hex::encode(session_keys.authority_discovery.0));
|
|
let slow_key = format!("0x{}", hex::encode(session_keys.slow_clap.0));
|
|
|
|
(gran_key, babe_key, audi_key, slow_key)
|
|
},
|
|
None => (String::new(), String::new(), String::new(), String::new()),
|
|
};
|
|
|
|
check_author_has_key(rpc_client, action_tx, &gran_key, "gran").await?;
|
|
check_author_has_key(rpc_client, action_tx, &babe_key, "babe").await?;
|
|
check_author_has_key(rpc_client, action_tx, &audi_key, "audi").await?;
|
|
check_author_has_key(rpc_client, action_tx, &slow_key, "slow").await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_queued_session_keys(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
rpc_client: &RpcClient,
|
|
account_id: &[u8; 32],
|
|
) -> Result<()> {
|
|
let account = super::raw_calls::convert_array_to_account_id(account_id);
|
|
let maybe_queued_keys = super::raw_calls::session::queued_keys(api, None).await?;
|
|
|
|
let (gran_key, babe_key, audi_key, slow_key) = match maybe_queued_keys {
|
|
Some(session_keys) => {
|
|
match session_keys.iter().find(|tuple| tuple.0 == account) {
|
|
Some(keys) => {
|
|
let session_keys = &keys.1;
|
|
let gran_key = format!("0x{}", hex::encode(session_keys.grandpa.0));
|
|
let babe_key = format!("0x{}", hex::encode(session_keys.babe.0));
|
|
let audi_key = format!("0x{}", hex::encode(session_keys.authority_discovery.0));
|
|
let slow_key = format!("0x{}", hex::encode(session_keys.slow_clap.0));
|
|
|
|
(gran_key, babe_key, audi_key, slow_key)
|
|
},
|
|
None => (String::new(), String::new(), String::new(), String::new()),
|
|
}
|
|
},
|
|
None => (String::new(), String::new(), String::new(), String::new()),
|
|
};
|
|
|
|
check_author_has_key(rpc_client, action_tx, &gran_key, "q_gran").await?;
|
|
check_author_has_key(rpc_client, action_tx, &babe_key, "q_babe").await?;
|
|
check_author_has_key(rpc_client, action_tx, &audi_key, "q_audi").await?;
|
|
check_author_has_key(rpc_client, action_tx, &slow_key, "q_slow").await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn check_author_has_key(
|
|
rpc_client: &RpcClient,
|
|
action_tx: &UnboundedSender<Action>,
|
|
key: &str,
|
|
name: &str,
|
|
) -> Result<()> {
|
|
let params_name = if name.starts_with("q_") {
|
|
&name[2..]
|
|
} else {
|
|
name
|
|
};
|
|
let is_stored: bool = rpc_client
|
|
.request("author_hasKey", rpc_params![key, params_name])
|
|
.await?;
|
|
let session_key_info = SessionKeyInfo {
|
|
key: key.to_string(),
|
|
is_stored
|
|
};
|
|
action_tx.send(Action::SetSessionKey(name.to_string(), session_key_info))?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_validator_staking_results(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
account_id: &[u8; 32],
|
|
) -> Result<()> {
|
|
let (start, end) = super::raw_calls::historical::stored_range(api, None)
|
|
.await?
|
|
.map(|range| {
|
|
(
|
|
range.0
|
|
.saturating_div(6)
|
|
.saturating_sub(1),
|
|
range.1
|
|
.saturating_div(6)
|
|
.saturating_sub(1),
|
|
)
|
|
})
|
|
.unwrap_or((0, 0));
|
|
for era_index in start..end {
|
|
get_validator_staking_result(action_tx, api, account_id, era_index).await?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_validator_staking_result(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
account_id: &[u8; 32],
|
|
era_index: u32,
|
|
) -> Result<()> {
|
|
get_validator_reward_in_era(action_tx, api, account_id, era_index).await?;
|
|
get_validator_claims_in_era(action_tx, api, account_id, era_index).await?;
|
|
get_validator_slashes_in_era(action_tx, api, account_id, era_index).await?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_current_validator_reward_in_era(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
) -> Result<()> {
|
|
let era_index = super::raw_calls::staking::active_era(api, None)
|
|
.await?
|
|
.map(|era_info| era_info.index)
|
|
.unwrap_or_default();
|
|
|
|
let disabled_validators = super::raw_calls::staking::disabled_validators(api, None)
|
|
.await?
|
|
.unwrap_or_default();
|
|
|
|
let maybe_era_reward_points = super::raw_calls::staking::eras_reward_points(api, None, era_index)
|
|
.await?;
|
|
|
|
let (total_points, individual) = match maybe_era_reward_points {
|
|
Some(era_reward_points) => {
|
|
(
|
|
era_reward_points.total,
|
|
era_reward_points.individual
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(index, (account_id, points))| {
|
|
let address = AccountId32::from(account_id.0)
|
|
.to_ss58check_with_version(Ss58AddressFormat::custom(1996));
|
|
EraRewardPoints {
|
|
address,
|
|
account_id: account_id.0,
|
|
points: *points,
|
|
disabled: disabled_validators.contains(&(index as u32)),
|
|
}
|
|
})
|
|
.collect(),
|
|
)
|
|
},
|
|
None => (0, Vec::new()),
|
|
};
|
|
|
|
action_tx.send(Action::SetCurrentValidatorEraRewards(
|
|
era_index, total_points, individual))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn get_validator_reward_in_era(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
account_id: &[u8; 32],
|
|
era_index: u32,
|
|
) -> Result<()> {
|
|
let maybe_era_reward_points = super::raw_calls::staking::eras_reward_points(api, None, era_index)
|
|
.await?;
|
|
|
|
let era_reward = super::raw_calls::staking::eras_validator_reward(api, None, era_index)
|
|
.await?
|
|
.unwrap_or_default();
|
|
|
|
let my_reward = match maybe_era_reward_points {
|
|
Some(era_reward_points) => {
|
|
let my_points = era_reward_points.individual
|
|
.iter()
|
|
.find(|(acc, _)| acc.0 == *account_id)
|
|
.map(|info| info.1)
|
|
.unwrap_or_default();
|
|
era_reward
|
|
.saturating_mul(my_points as u128)
|
|
.saturating_div(era_reward_points.total as u128)
|
|
},
|
|
None => 0u128,
|
|
};
|
|
|
|
action_tx.send(Action::SetValidatorEraReward(era_index, my_reward))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn get_validator_claims_in_era(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
account_id: &[u8; 32],
|
|
era_index: u32,
|
|
) -> Result<()> {
|
|
let maybe_claimed_rewards = super::raw_calls::staking::claimed_rewards(api, None, era_index, account_id)
|
|
.await?;
|
|
|
|
if let Some(claimed_rewards) = maybe_claimed_rewards {
|
|
let already_claimed = claimed_rewards
|
|
.first()
|
|
.map(|x| *x == 1)
|
|
.unwrap_or(false);
|
|
action_tx.send(Action::SetValidatorEraClaimed(era_index, already_claimed))?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn get_validator_slashes_in_era(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
account_id: &[u8; 32],
|
|
era_index: u32,
|
|
) -> Result<()> {
|
|
let maybe_slash_in_era = super::raw_calls::staking::validator_slash_in_era(api, None, era_index, account_id)
|
|
.await?;
|
|
|
|
if let Some(slash_in_era) = maybe_slash_in_era {
|
|
action_tx.send(Action::SetValidatorEraSlash(era_index, slash_in_era.1))?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_validators_ledger(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
account_id: &[u8; 32],
|
|
) -> Result<()> {
|
|
let maybe_ledger = super::raw_calls::staking::ledger(api, None, account_id)
|
|
.await?;
|
|
|
|
if let Some(ledger) = maybe_ledger {
|
|
action_tx.send(Action::SetStakedAmountRatio(ledger.total, ledger.active))?;
|
|
for chunk in ledger.unlocking.0.iter() {
|
|
action_tx.send(Action::SetValidatorEraUnlocking(chunk.era, chunk.value))?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_nominators_by_validator(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
account_id: &[u8; 32],
|
|
) -> Result<()> {
|
|
let active_era_index = super::raw_calls::staking::active_era(api, None)
|
|
.await?
|
|
.map(|era_info| era_info.index)
|
|
.unwrap_or_default();
|
|
|
|
let maybe_eras_stakers = super::raw_calls::staking::eras_stakers(api, None, active_era_index, account_id)
|
|
.await?;
|
|
|
|
let nominators = match maybe_eras_stakers {
|
|
Some(eras_stakers) => eras_stakers
|
|
.others
|
|
.iter()
|
|
.map(|info| {
|
|
Nominator {
|
|
who: AccountId32::from(info.who.0)
|
|
.to_ss58check_with_version(Ss58AddressFormat::custom(1996)),
|
|
value: info.value,
|
|
}
|
|
})
|
|
.collect::<Vec<_>>(),
|
|
None => Vec::new(),
|
|
};
|
|
|
|
action_tx.send(Action::SetNominatorsByValidator(nominators))?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_is_stash_bonded(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
account_id: &[u8; 32],
|
|
) -> Result<()> {
|
|
let is_bonded = super::raw_calls::staking::bonded(api, None, account_id)
|
|
.await?
|
|
.is_some();
|
|
action_tx.send(Action::SetBondedAmount(is_bonded))?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_staking_value_ratio(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
account_id: &[u8; 32],
|
|
) -> Result<()> {
|
|
let active_era_index = super::raw_calls::staking::active_era(api, None)
|
|
.await?
|
|
.map(|era_info| era_info.index)
|
|
.unwrap_or_default();
|
|
let maybe_era_stakers_overview = super::raw_calls::staking::eras_stakers_overview(api, None, active_era_index, account_id)
|
|
.await?;
|
|
let (total, own) = match maybe_era_stakers_overview {
|
|
Some(overview) => (overview.total, overview.own),
|
|
None => (0, 0),
|
|
};
|
|
action_tx.send(Action::SetStakedRatio(total, own))?;
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get_validator_prefs(
|
|
action_tx: &UnboundedSender<Action>,
|
|
api: &OnlineClient<CasperConfig>,
|
|
account_id: &[u8; 32],
|
|
) -> Result<()> {
|
|
let maybe_validator_prefs = super::raw_calls::staking::validators(api, None, account_id)
|
|
.await?;
|
|
let (comission, blocked) = match maybe_validator_prefs {
|
|
Some(prefs) => (prefs.commission.0, prefs.blocked),
|
|
None => (0, false),
|
|
};
|
|
|
|
action_tx.send(Action::SetValidatorPrefs(comission, blocked))?;
|
|
Ok(())
|
|
}
|