Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PoS: re-usable bonds and unbonds query #1518

Merged
merged 3 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 29 additions & 46 deletions apps/src/lib/client/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ use namada::ledger::pos::{
self, BondId, BondsAndUnbondsDetail, CommissionPair, PosParams, Slash,
};
use namada::ledger::queries::RPC;
use namada::ledger::rpc::{query_epoch, TxResponse};
use namada::ledger::rpc::{
enriched_bonds_and_unbonds, query_epoch, TxResponse,
};
use namada::ledger::storage::ConversionState;
use namada::ledger::wallet::{AddressVpType, Wallet};
use namada::proof_of_stake::types::WeightedValidator;
Expand Down Expand Up @@ -1252,29 +1254,18 @@ pub async fn query_bonds<C: namada::ledger::queries::Client + Sync>(
_wallet: &mut Wallet<CliWalletUtils>,
args: args::QueryBonds,
) -> std::io::Result<()> {
let _epoch = query_and_print_epoch(client).await;
let epoch = query_and_print_epoch(client).await;

let source = args.owner;
let validator = args.validator;

let stdout = io::stdout();
let mut w = stdout.lock();

let bonds_and_unbonds: pos::types::BondsAndUnbondsDetails =
unwrap_client_response::<C, pos::types::BondsAndUnbondsDetails>(
RPC.vp()
.pos()
.bonds_and_unbonds(client, &source, &validator)
.await,
);
let mut bonds_total: token::Amount = 0.into();
let mut bonds_total_slashed: token::Amount = 0.into();
let mut unbonds_total: token::Amount = 0.into();
let mut unbonds_total_slashed: token::Amount = 0.into();
let mut total_withdrawable: token::Amount = 0.into();
for (bond_id, details) in bonds_and_unbonds {
let mut total: token::Amount = 0.into();
let mut total_slashed: token::Amount = 0.into();
let bonds_and_unbonds =
enriched_bonds_and_unbonds(client, epoch, &source, &validator).await;

for (bond_id, details) in &bonds_and_unbonds.data {
let bond_type = if bond_id.source == bond_id.validator {
format!("Self-bonds from {}", bond_id.validator)
} else {
Expand All @@ -1284,74 +1275,66 @@ pub async fn query_bonds<C: namada::ledger::queries::Client + Sync>(
)
};
writeln!(w, "{}:", bond_type)?;
for bond in details.bonds {
for bond in &details.data.bonds {
writeln!(
w,
" Remaining active bond from epoch {}: Δ {}",
bond.start, bond.amount
)?;
total += bond.amount;
total_slashed += bond.slashed_amount.unwrap_or_default();
}
if total_slashed != token::Amount::default() {
if details.bonds_total_slashed != token::Amount::default() {
writeln!(
w,
"Active (slashed) bonds total: {}",
total - total_slashed
details.bonds_total_active()
)?;
}
writeln!(w, "Bonds total: {}", total)?;
writeln!(w, "Bonds total: {}", details.bonds_total)?;
writeln!(w)?;
bonds_total += total;
bonds_total_slashed += total_slashed;

let mut withdrawable = token::Amount::default();
if !details.unbonds.is_empty() {
let mut total: token::Amount = 0.into();
let mut total_slashed: token::Amount = 0.into();
if !details.data.unbonds.is_empty() {
let bond_type = if bond_id.source == bond_id.validator {
format!("Unbonded self-bonds from {}", bond_id.validator)
} else {
format!("Unbonded delegations from {}", bond_id.source)
};
writeln!(w, "{}:", bond_type)?;
for unbond in details.unbonds {
total += unbond.amount;
total_slashed += unbond.slashed_amount.unwrap_or_default();
for unbond in &details.data.unbonds {
writeln!(
w,
" Withdrawable from epoch {} (active from {}): Δ {}",
unbond.withdraw, unbond.start, unbond.amount
)?;
}
withdrawable = total - total_slashed;
writeln!(w, "Unbonded total: {}", total)?;

unbonds_total += total;
unbonds_total_slashed += total_slashed;
total_withdrawable += withdrawable;
writeln!(w, "Unbonded total: {}", details.unbonds_total)?;
}
writeln!(w, "Withdrawable total: {}", withdrawable)?;
writeln!(w, "Withdrawable total: {}", details.total_withdrawable)?;
writeln!(w)?;
}
if bonds_total != bonds_total_slashed {
if bonds_and_unbonds.bonds_total != bonds_and_unbonds.bonds_total_slashed {
writeln!(
w,
"All bonds total active: {}",
bonds_total - bonds_total_slashed
bonds_and_unbonds.bonds_total_active()
)?;
}
writeln!(w, "All bonds total: {}", bonds_total)?;
writeln!(w, "All bonds total: {}", bonds_and_unbonds.bonds_total)?;

if unbonds_total != unbonds_total_slashed {
if bonds_and_unbonds.unbonds_total
!= bonds_and_unbonds.unbonds_total_slashed
{
writeln!(
w,
"All unbonds total active: {}",
unbonds_total - unbonds_total_slashed
bonds_and_unbonds.unbonds_total_active()
)?;
}
writeln!(w, "All unbonds total: {}", unbonds_total)?;
writeln!(w, "All unbonds total withdrawable: {}", total_withdrawable)?;
writeln!(w, "All unbonds total: {}", bonds_and_unbonds.unbonds_total)?;
writeln!(
w,
"All unbonds total withdrawable: {}",
bonds_and_unbonds.total_withdrawable
)?;
Ok(())
}

Expand Down
6 changes: 2 additions & 4 deletions shared/src/ledger/queries/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ pub use types::Client;
pub use types::{
EncodedResponseQuery, RequestCtx, RequestQuery, ResponseQuery, Router,
};
use vp::VP;
// Re-export to show in rustdoc!
pub use vp::{Pos, Vp};
use vp::{Vp, VP};

use super::storage::{DBIter, StorageHasher, DB};
use super::storage_api;
Expand All @@ -22,7 +20,7 @@ use crate::types::storage::BlockHeight;
mod router;
mod shell;
mod types;
mod vp;
pub mod vp;

// Most commonly expected patterns should be declared first
router! {RPC,
Expand Down
5 changes: 4 additions & 1 deletion shared/src/ledger/queries/vp/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
//! Queries router and handlers for validity predicates

// Re-export to show in rustdoc!
pub use pos::Pos;
use pos::POS;
mod pos;
pub mod pos;

// Validity predicate queries
router! {VP,
( "pos" ) = (sub POS),
}

/// Client-only methods for the router type are composed from router functions.
#[cfg(any(test, feature = "async-client"))]
pub mod client_only_methods {
#[cfg(not(feature = "mainnet"))]
Expand Down
140 changes: 139 additions & 1 deletion shared/src/ledger/queries/vp/pos.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
//! Queries router and handlers for PoS validity predicate

use std::collections::{HashMap, HashSet};

use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
use namada_core::ledger::storage_api::collections::lazy_map;
use namada_core::ledger::storage_api::OptionExt;
use namada_proof_of_stake::types::{
BondId, BondsAndUnbondsDetails, CommissionPair, Slash, WeightedValidator,
BondId, BondsAndUnbondsDetail, BondsAndUnbondsDetails, CommissionPair,
Slash, WeightedValidator,
};
use namada_proof_of_stake::{
self, below_capacity_validator_set_handle, bond_amount, bond_handle,
Expand Down Expand Up @@ -87,6 +91,46 @@ router! {POS,

}

/// Enriched bonds data with extra information calculated from the data queried
/// from the node.
#[derive(Debug, Clone, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct Enriched<T> {
/// The queried data
pub data: T,
/// Sum of the bond amounts
pub bonds_total: token::Amount,
/// Sum of the bond slashed amounts
pub bonds_total_slashed: token::Amount,
/// Sum of the unbond amounts
pub unbonds_total: token::Amount,
/// Sum of the unbond slashed amounts
pub unbonds_total_slashed: token::Amount,
/// Sum ofthe withdrawable amounts
pub total_withdrawable: token::Amount,
}

/// Bonds and unbonds with all details (slashes and rewards, if any) grouped by
/// their bond IDs enriched with extra information calculated from the data
/// queried from the node.
pub type EnrichedBondsAndUnbondsDetails =
Enriched<HashMap<BondId, EnrichedBondsAndUnbondsDetail>>;

/// Bonds and unbonds with all details (slashes and rewards, if any) enriched
/// with extra information calculated from the data queried from the node.
pub type EnrichedBondsAndUnbondsDetail = Enriched<BondsAndUnbondsDetail>;

impl<T> Enriched<T> {
/// The bonds amount reduced by slashes
pub fn bonds_total_active(&self) -> token::Amount {
self.bonds_total - self.bonds_total_slashed
}

/// The unbonds amount reduced by slashes
pub fn unbonds_total_active(&self) -> token::Amount {
self.unbonds_total - self.unbonds_total_slashed
}
}

// Handlers that implement the functions via `trait StorageRead`:

/// Find if the given address belongs to a validator account.
Expand Down Expand Up @@ -473,3 +517,97 @@ where
{
namada_proof_of_stake::find_validator_by_raw_hash(ctx.wl_storage, tm_addr)
}

/// Client-only methods for the router type are composed from router functions.
#[cfg(any(test, feature = "async-client"))]
pub mod client_only_methods {
use super::*;
use crate::ledger::queries::{Client, RPC};

impl Pos {
/// Get bonds and unbonds with all details (slashes and rewards, if any)
/// grouped by their bond IDs, enriched with extra information
/// calculated from the data.
pub async fn enriched_bonds_and_unbonds<CLIENT>(
&self,
client: &CLIENT,
current_epoch: Epoch,
source: &Option<Address>,
validator: &Option<Address>,
) -> Result<EnrichedBondsAndUnbondsDetails, <CLIENT as Client>::Error>
where
CLIENT: Client + Sync,
{
let data = RPC
.vp()
.pos()
.bonds_and_unbonds(client, source, validator)
.await?;
Ok(enrich_bonds_and_unbonds(current_epoch, data))
}
}
}

/// Calculate extra information from the bonds and unbonds details.
fn enrich_bonds_and_unbonds(
current_epoch: Epoch,
bonds_and_unbonds: BondsAndUnbondsDetails,
) -> EnrichedBondsAndUnbondsDetails {
let mut bonds_total: token::Amount = 0.into();
let mut bonds_total_slashed: token::Amount = 0.into();
let mut unbonds_total: token::Amount = 0.into();
let mut unbonds_total_slashed: token::Amount = 0.into();
let mut total_withdrawable: token::Amount = 0.into();

let enriched_details: HashMap<BondId, EnrichedBondsAndUnbondsDetail> =
bonds_and_unbonds
.into_iter()
.map(|(bond_id, detail)| {
let mut bond_total: token::Amount = 0.into();
let mut bond_total_slashed: token::Amount = 0.into();
let mut unbond_total: token::Amount = 0.into();
let mut unbond_total_slashed: token::Amount = 0.into();
let mut withdrawable: token::Amount = 0.into();

for bond in &detail.bonds {
bond_total += bond.amount;
bond_total_slashed +=
bond.slashed_amount.unwrap_or_default();
}
for unbond in &detail.unbonds {
unbond_total += unbond.amount;
unbond_total_slashed +=
unbond.slashed_amount.unwrap_or_default();

if current_epoch >= unbond.withdraw {
withdrawable += unbond.amount
- unbond.slashed_amount.unwrap_or_default()
}
}

bonds_total += bond_total;
bonds_total_slashed += bond_total_slashed;
unbonds_total += unbond_total;
unbonds_total_slashed += unbond_total_slashed;
total_withdrawable += withdrawable;

let enriched_detail = EnrichedBondsAndUnbondsDetail {
data: detail,
bonds_total: bond_total,
bonds_total_slashed: bond_total_slashed,
unbonds_total: unbond_total,
unbonds_total_slashed: unbond_total_slashed,
total_withdrawable: withdrawable,
};
(bond_id, enriched_detail)
})
.collect();
EnrichedBondsAndUnbondsDetails {
data: enriched_details,
bonds_total,
bonds_total_slashed,
unbonds_total,
unbonds_total_slashed,
total_withdrawable,
}
}
42 changes: 41 additions & 1 deletion shared/src/ledger/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ use namada_core::ledger::testnet_pow;
use namada_core::types::address::Address;
use namada_core::types::storage::Key;
use namada_core::types::token::Amount;
use namada_proof_of_stake::types::CommissionPair;
use namada_proof_of_stake::types::{BondsAndUnbondsDetails, CommissionPair};
use serde::Serialize;
use tokio::time::Duration;

use crate::ledger::events::Event;
use crate::ledger::governance::parameters::GovParams;
use crate::ledger::governance::storage as gov_storage;
use crate::ledger::native_vp::governance::utils::Votes;
use crate::ledger::queries::vp::pos::EnrichedBondsAndUnbondsDetails;
use crate::ledger::queries::RPC;
use crate::proto::Tx;
use crate::tendermint::merkle::proof::Proof;
Expand Down Expand Up @@ -885,3 +886,42 @@ pub async fn get_bond_amount_at<C: crate::ledger::queries::Client + Sync>(
);
Some(total_active)
}

/// Get bonds and unbonds with all details (slashes and rewards, if any)
/// grouped by their bond IDs.
pub async fn bonds_and_unbonds<C: crate::ledger::queries::Client + Sync>(
client: &C,
source: &Option<Address>,
validator: &Option<Address>,
) -> BondsAndUnbondsDetails {
unwrap_client_response::<C, _>(
RPC.vp()
.pos()
.bonds_and_unbonds(client, source, validator)
.await,
)
}

/// Get bonds and unbonds with all details (slashes and rewards, if any)
/// grouped by their bond IDs, enriched with extra information calculated from
/// the data.
pub async fn enriched_bonds_and_unbonds<
C: crate::ledger::queries::Client + Sync,
>(
client: &C,
current_epoch: Epoch,
source: &Option<Address>,
validator: &Option<Address>,
) -> EnrichedBondsAndUnbondsDetails {
unwrap_client_response::<C, _>(
RPC.vp()
.pos()
.enriched_bonds_and_unbonds(
client,
current_epoch,
source,
validator,
)
.await,
)
}