From 5e83e78c6c31b2f756898c56a3153b0aa07ae33b Mon Sep 17 00:00:00 2001 From: zenchainlabs Date: Sat, 13 Aug 2022 11:42:56 -0600 Subject: [PATCH 1/2] Added tendermint routes to REST some queries and added more fields to validator cmd --- rest/src/main.rs | 211 ++++++++++++++++++++++++++++++++++++++++------- src/bin/nomic.rs | 8 +- 2 files changed, 184 insertions(+), 35 deletions(-) diff --git a/rest/src/main.rs b/rest/src/main.rs index be41bf46..f6ada16a 100644 --- a/rest/src/main.rs +++ b/rest/src/main.rs @@ -8,6 +8,14 @@ use nomic::{app_client, app::{Nom, InnerApp, CHAIN_ID}, orga::{query::Query, coi use tendermint_rpc as tm; use tm::Client as _; +#[derive(Debug, Serialize, Deserialize)] +struct DeclareInfo { + moniker: String, + website: String, + identity: String, + details: String, +} + #[get("/cosmos/bank/v1beta1/balances/
")] async fn bank_balances(address: &str) -> Result> { let address: Address = address.parse().unwrap(); @@ -18,6 +26,12 @@ async fn bank_balances(address: &str) -> Result> { .map_err(|e| BadRequest(Some(format!("{:?}", e))))? .into(); + let btcbalance: u64 = app_client().bitcoin.accounts.balance(address) + .await + .map_err(|e| BadRequest(Some(format!("{:?}", e))))? + .map_err(|e| BadRequest(Some(format!("{:?}", e))))? + .into(); + Ok(json!({ "balances": [ { @@ -26,7 +40,7 @@ async fn bank_balances(address: &str) -> Result> { }, { "denom": "nsat", - "amount": balance.to_string(), + "amount": btcbalance.to_string(), } ], "pagination": { @@ -211,32 +225,6 @@ async fn query(query: &str) -> Result> { Ok(base64::encode(res.value)) } -#[get("/cosmos/staking/v1beta1/delegations/
")] -async fn staking_delegators_delegations(address: &str) -> Result> { - let address: Address = address.parse().unwrap(); - - let delegations = app_client().staking.delegations(address) - .await - .map_err(|e| BadRequest(Some(format!("{:?}", e))))? - .map_err(|e| BadRequest(Some(format!("{:?}", e))))?; - - let total_staked: u64 = delegations.iter().map(|(_, d)| -> u64 { d.staked.into() }).sum(); - - Ok(json!({ "delegation_responses": [ - { - "delegation": { - "delegator_address": "", - "validator_address": "", - "shares": "0" - }, - "balance": { - "denom": "unom", - "amount": total_staked.to_string(), - } - } - ], "pagination": { "next_key": null, "total": "0" } })) -} - #[get("/staking/delegators/
/delegations")] async fn staking_delegators_delegations_2(address: &str) -> Result> { let address: Address = address.parse().unwrap(); @@ -452,6 +440,166 @@ fn ibc_applications_transfer_params() -> Value { }) } +#[get("/cosmos/staking/v1beta1/delegations/
")] +async fn staking_delegators_delegations(address: &str) -> Result> { + let address: Address = address.parse().unwrap(); + + let delegations = app_client().staking.delegations(address) + .await + .map_err(|e| BadRequest(Some(format!("{:?}", e))))? + .map_err(|e| BadRequest(Some(format!("{:?}", e))))?; + + let total_staked: u64 = delegations.iter().map(|(_, d)| -> u64 { d.staked.into() }).sum(); + + let mut valarray = Vec::new(); + + for (validator, delegation) in delegations { + let staked = delegation.staked; + let liquid: u64 = delegation + .liquid + .iter() + .map(|(_, amount)| -> u64 { (*amount).into() }) + .sum(); + if staked == 0 && liquid == 0 { + continue; + } + + use nomic::app::Nom; + use nomic::bitcoin::Nbtc; + use nomic::orga::coins::Symbol; + + let liquid_nom = delegation + .liquid + .iter() + .find(|(denom, _)| *denom == Nom::INDEX) + .unwrap() + .1; + let liquid_nbtc = delegation + .liquid + .iter() + .find(|(denom, _)| *denom == Nbtc::INDEX) + .unwrap_or(&(0, 0.into())) + .1; + + let valaddr = validator.to_string(); + let staked = staked.to_string(); + + let mut owned_string: String = "validator:".to_owned(); + let borrowed_string: &str = &validator.to_string(); + + owned_string.push_str(borrowed_string); + + + let full_name = "John Doe"; + let age_last_year = 42; + let ranphone = 23456; + + let data = json!({ + "_delegation": { + "_delegator_address": &address.to_string(), + "_validator_address": &validator.to_string(), + "shares": staked.to_string() + }, + "balance": { + "_denom": "unom", + "amount": staked.to_string() + } + }); + + valarray.push(data); + } + Ok(json!({ "_delegation_responses": &valarray, "total_staked": total_staked.to_string(), "pagination": {"next_key": null, "total": "0"} })) + +} + +#[get("/cosmos/staking/v1beta1/validators")] +async fn staking_validators() -> Result> { + let validators = app_client().staking.all_validators() + .await + .map_err(|e| BadRequest(Some(format!("{:?}", e))))? + .map_err(|e| BadRequest(Some(format!("{:?}", e))))?; + + let mut fullarray = Vec::new(); + + for validator in validators { + let info: DeclareInfo = + serde_json::from_slice(validator.info.bytes.as_slice()).unwrap(); + + let data = json!({ + "active": validator.in_active_set.to_string(), + "operator_address": validator.address.to_string(), + "tokens": validator.amount_staked.to_string(), + "jailed": validator.jailed.to_string(), + "min_self_delegation": validator.min_self_delegation.to_string(), + "description": { + "moniker": info.moniker.to_string(), + "identity": info.identity.to_string(), + "website": info.website.to_string(), + "details": info.details.to_string(), + }, + "commission": { + "commission_rates": { + "rate": validator.commission.rate.to_string(), + "max_rate": validator.commission.max.to_string(), + "max_change_rate": validator.commission.max_change.to_string() + } + }, + "unbonding": validator.unbonding.to_string(), + "unbonding_time": validator.unbonding_start_seconds.to_string(), + "tombstoned": validator.tombstoned.to_string() + }); + + serde_json::to_string_pretty(&fullarray.push(data)); + + } + + Ok(json!({ "validators": fullarray })) +} + +#[get("/staking/validators")] +async fn staking_validators2() -> Result> { + let validators = app_client().staking.all_validators() + .await + .map_err(|e| BadRequest(Some(format!("{:?}", e))))? + .map_err(|e| BadRequest(Some(format!("{:?}", e))))?; + + let mut fullarray = Vec::new(); + + for validator in validators { + let info: DeclareInfo = + serde_json::from_slice(validator.info.bytes.as_slice()).unwrap(); + + let data = json!({ + "operator_address": validator.address.to_string(), + "tokens": validator.amount_staked.to_string(), + "jailed": validator.jailed.to_string(), + "min_self_delegation": validator.min_self_delegation.to_string(), + "description": { + "moniker": info.moniker.to_string(), + "identity": info.identity.to_string(), + "website": info.website.to_string(), + "details": info.details.to_string(), + }, + "commission": { + "commission_rates": { + "rate": validator.commission.rate.to_string(), + "max_rate": validator.commission.max.to_string(), + "max_change_rate": validator.commission.max_change.to_string() + } + }, + "unbonding": validator.unbonding.to_string(), + "unbonding_time": validator.unbonding_start_seconds.to_string(), + "tombstoned": validator.tombstoned.to_string() + + }); + + serde_json::to_string_pretty(&fullarray.push(data)); + + } + + Ok(json!({ "validators": fullarray })) +} + use rocket::http::Header; use rocket::{Request, Response}; use rocket::fairing::{Fairing, Info, Kind}; @@ -483,9 +631,7 @@ fn rocket() -> _ { auth_accounts, txs, txs2, - query, - staking_delegators_delegations, - // staking_delegators_delegations_2, + query, staking_delegators_unbonding_delegations, staking_delegators_unbonding_delegations_2, distribution_delegatrs_rewards, @@ -498,5 +644,8 @@ fn rocket() -> _ { ibc_apps_transfer_params, ibc_applications_transfer_params, bank_supply_unom, + staking_delegators_delegations, + staking_validators, + staking_validators2, ]) -} +} \ No newline at end of file diff --git a/src/bin/nomic.rs b/src/bin/nomic.rs index 12e60cb8..5faf06fe 100644 --- a/src/bin/nomic.rs +++ b/src/bin/nomic.rs @@ -483,10 +483,10 @@ impl ValidatorsCmd { for validator in validators { let info: DeclareInfo = serde_json::from_slice(validator.info.bytes.as_slice()).unwrap(); - println!( - "- {}\n\tVOTING POWER: {}\n\tMONIKER: {}\n\tDETAILS: {}", - validator.address, validator.amount_staked, info.moniker, info.details - ); + println!( + "- {}\n\tVOTING POWER: {}\n\tMONIKER: {}\n\tCOMMISSION: {}\n\tDETAILS: {}\n\tJAILED: {}\n\tTOMBSTONED: {}\n\tACTIVE: {}\n\tUNBONDING: {}\n\tUNBONDING_START: {}", + validator.address, validator.amount_staked, info.moniker, validator.commission.rate, info.details, validator.jailed, validator.tombstoned, validator.in_active_set, validator.unbonding, validator.unbonding_start_seconds + ); } Ok(()) From 18b3deaa131b3ab5449abfb20ab7fd15679be15e Mon Sep 17 00:00:00 2001 From: Judd Keppel Date: Wed, 30 Aug 2023 16:26:35 -0500 Subject: [PATCH 2/2] Add upgrade status command --- src/app.rs | 4 +- src/bin/nomic.rs | 182 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+), 2 deletions(-) diff --git a/src/app.rs b/src/app.rs index 34277fd0..d694e7d2 100644 --- a/src/app.rs +++ b/src/app.rs @@ -87,7 +87,7 @@ pub struct InnerApp { pub ibc: Ibc, #[orga(version(V1, V2))] - upgrade: Upgrade, + pub upgrade: Upgrade, #[orga(version(V2))] #[call] @@ -965,7 +965,7 @@ impl RewardTimer { } } -fn in_upgrade_window(now_seconds: i64) -> bool { +pub fn in_upgrade_window(now_seconds: i64) -> bool { use chrono::prelude::*; let now = Utc.timestamp_opt(now_seconds, 0).unwrap(); diff --git a/src/bin/nomic.rs b/src/bin/nomic.rs index 5945a790..da848153 100644 --- a/src/bin/nomic.rs +++ b/src/bin/nomic.rs @@ -96,6 +96,7 @@ pub enum Command { #[cfg(feature = "testnet")] IbcTransfer(IbcTransferCmd), Export(ExportCmd), + UpgradeStatus(UpgradeStatusCmd), } impl Command { @@ -148,6 +149,7 @@ impl Command { #[cfg(feature = "testnet")] IbcTransfer(cmd) => cmd.run().await, Export(cmd) => cmd.run().await, + UpgradeStatus(cmd) => cmd.run().await, } }) } @@ -1375,6 +1377,186 @@ impl ExportCmd { } } +#[derive(Parser, Debug)] +pub struct UpgradeStatusCmd {} + +impl UpgradeStatusCmd { + async fn run(&self) -> Result<()> { + use orga::coins::staking::ValidatorQueryInfo; + use orga::coins::VersionedAddress; + use std::collections::{HashMap, HashSet}; + let client = app_client(); + let tm_client = tendermint_rpc::HttpClient::new("http://localhost:26657").unwrap(); + let curr_height = tm_client + .status() + .await + .unwrap() + .sync_info + .latest_block_height; + let validators = tm_client + .validators(curr_height, tendermint_rpc::Paging::All) + .await + .unwrap() + .validators; + + let mut vp_map: HashMap<[u8; 32], u64> = HashMap::new(); + let mut total_vp = 0; + for validator in validators { + vp_map.insert( + validator.pub_key.to_bytes().try_into().unwrap(), + validator.power(), + ); + total_vp += validator.power(); + } + + let (delay_seconds, threshold, current_version) = client + .query(|app: InnerApp| { + let current_version = app.upgrade.current_version.get(())?.unwrap(); + Ok(( + app.upgrade.activation_delay_seconds, + app.upgrade.threshold, + current_version.to_vec(), + )) + }) + .await?; + + let next_version: orga::upgrade::Version = vec![current_version[0] + 1].try_into().unwrap(); + let mut signals: Vec<([u8; 32], i64)> = client + .query(|app: InnerApp| { + let mut signals = vec![]; + for entry in app.upgrade.signals.iter()? { + let (pubkey, signal) = entry?; + if signal.version == next_version { + signals.push((*pubkey, signal.time)); + } + } + Ok(signals) + }) + .await?; + + signals.sort_by(|a, b| a.1.cmp(&b.1)); + let mut signaled_vp = 0; + let mut activation_time = None; + let threshold: f64 = threshold.to_string().parse().unwrap(); + + for (pubkey, time) in signals.iter() { + signaled_vp += vp_map.get(pubkey).unwrap_or(&0); + let frac = signaled_vp as f64 / total_vp as f64; + if frac >= threshold && activation_time.is_none() { + activation_time.replace(time + delay_seconds); + } + } + let frac = signaled_vp as f64 / total_vp as f64; + + if frac < 0.01 { + println!("No upgrade in progress"); + return Ok(()); + } + + let all_validators: Vec = client + .query(|app: InnerApp| app.staking.all_validators()) + .await?; + + let mut validator_names: HashMap = + HashMap::new(); + all_validators + .into_iter() + .filter(|v| v.in_active_set) + .for_each(|v| { + let bytes: Vec = v.info.into(); + let name = if let Ok(info) = + serde_json::from_slice::<'_, serde_json::Value>(bytes.as_slice()) + { + info.get("moniker") + .and_then(|v| v.as_str()) + .unwrap_or(v.address.to_string().as_str()) + .to_string() + } else { + v.address.to_string() + }; + + validator_names.insert(v.address, (name, v.amount_staked.into())); + }); + + let mut consensus_keys: HashMap = HashMap::new(); + for (address, _) in validator_names.iter() { + let consensus_key = client + .query(|app: InnerApp| app.staking.consensus_key((*address).into())) + .await?; + consensus_keys.insert(*address, consensus_key); + } + let mut signaled_cons_keys: HashSet<[u8; 32]> = HashSet::new(); + + for (cons_key, _) in signals.iter() { + signaled_cons_keys.insert(*cons_key); + } + + let mut entries = validator_names.iter().collect::>(); + entries.sort_by(|(_, (_, a)), (_, (_, b))| b.cmp(a)); + + println!(""); + println!("Upgraded:"); + for (addr, (name, power)) in entries.iter() { + let cons_key = consensus_keys.get(addr).unwrap(); + if signaled_cons_keys.contains(cons_key) { + println!( + "✅ {} ({:.2}%)", + name, + (*power as f64 / total_vp as f64) * 100.0 + ); + } + } + println!(""); + println!("Not upgraded:"); + for (addr, (name, power)) in entries.iter() { + let cons_key = consensus_keys.get(addr).unwrap(); + if !signaled_cons_keys.contains(cons_key) { + println!( + "❌ {} ({:.2}%)", + name, + (*power as f64 / total_vp as f64) * 100.0 + ); + } + } + println!(""); + + println!( + "Upgrade has been signaled by {:.2}% of voting power", + frac * 100.0 + ); + + if let Some(t) = activation_time { + use chrono::prelude::*; + let mut activation_date = chrono::Utc.timestamp_opt(t, 0).unwrap(); + if activation_date.hour() > 17 + || activation_date.hour() == 17 && activation_date.minute() >= 10 + { + activation_date = activation_date + .checked_add_days(chrono::Days::new(1)) + .unwrap(); + } + activation_date = activation_date + .with_hour(17) + .unwrap() + .with_minute(0) + .unwrap() + .with_second(0) + .unwrap(); + + while !nomic::app::in_upgrade_window(activation_date.timestamp()) { + activation_date = activation_date + .checked_add_days(chrono::Days::new(1)) + .unwrap(); + } + println!("Upgrade will activate at {}", activation_date); + } else { + println!("Upgrade requires {:.2}% of voting power", threshold * 100.0); + } + + Ok(()) + } +} + pub fn main() { if std::env::var("NOMIC_LOG_SIMPLE").is_ok() { pretty_env_logger::formatted_builder()