diff --git a/.changelog/unreleased/improvements/3899-get-validators-livenesses.md b/.changelog/unreleased/improvements/3899-get-validators-livenesses.md new file mode 100644 index 0000000000..9b94cc1590 --- /dev/null +++ b/.changelog/unreleased/improvements/3899-get-validators-livenesses.md @@ -0,0 +1,2 @@ +- SDK query to get liveness information for the network and consensus validator + set. ([\#3899](https://github.com/anoma/namada/pull/3899)) \ No newline at end of file diff --git a/crates/proof_of_stake/src/types/mod.rs b/crates/proof_of_stake/src/types/mod.rs index bff68a04bb..988d1a7396 100644 --- a/crates/proof_of_stake/src/types/mod.rs +++ b/crates/proof_of_stake/src/types/mod.rs @@ -329,6 +329,28 @@ pub struct Redelegation { /// Redelegation amount pub amount: token::Amount, } + +/// Some liveness data for a consensus validator +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, BorshDeserializer)] +pub struct ValidatorLiveness { + /// Validator address + pub native_address: Address, + /// CometBFT address + pub comet_address: String, + /// Validator missed votes + pub missed_votes: u64, +} + +/// Liveness data related to the network and set of consensus validators +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, BorshDeserializer)] +pub struct LivenessInfo { + /// Length of liveness window + pub liveness_window_len: u64, + /// Liveness threshold + pub liveness_threshold: Dec, + /// Validators' liveness info + pub validators: Vec, +} // -------------------------------------------------------------------------------------------- /// A genesis validator definition. diff --git a/crates/sdk/src/queries/vp/pos.rs b/crates/sdk/src/queries/vp/pos.rs index 73aad5ae90..0076419c73 100644 --- a/crates/sdk/src/queries/vp/pos.rs +++ b/crates/sdk/src/queries/vp/pos.rs @@ -7,7 +7,7 @@ use namada_core::address::Address; use namada_core::arith::{self, checked}; use namada_core::chain::Epoch; use namada_core::collections::{HashMap, HashSet}; -use namada_core::key::common; +use namada_core::key::{common, tm_consensus_key_raw_hash}; use namada_core::token; use namada_proof_of_stake::parameters::PosParams; use namada_proof_of_stake::queries::{ @@ -17,8 +17,10 @@ use namada_proof_of_stake::slashing::{ find_all_enqueued_slashes, find_all_slashes, }; use namada_proof_of_stake::storage::{ - bond_handle, read_all_validator_addresses, + bond_handle, get_consensus_key, liveness_sum_missed_votes_handle, + read_all_validator_addresses, read_below_capacity_validator_set_addresses_with_stake, + read_consensus_validator_set_addresses, read_consensus_validator_set_addresses_with_stake, read_pos_params, read_total_active_stake, read_total_stake, read_validator_avatar, read_validator_description, read_validator_discord_handle, @@ -31,7 +33,8 @@ use namada_proof_of_stake::storage::{ pub use namada_proof_of_stake::types::ValidatorStateInfo; use namada_proof_of_stake::types::{ BondId, BondsAndUnbondsDetail, BondsAndUnbondsDetails, CommissionPair, - Slash, ValidatorMetaData, WeightedValidator, + LivenessInfo, Slash, ValidatorLiveness, ValidatorMetaData, + WeightedValidator, }; use namada_proof_of_stake::{bond_amount, query_reward_tokens}; use namada_state::{DBIter, KeySeg, StorageHasher, DB}; @@ -51,6 +54,8 @@ router! {POS, ( "addresses" / [epoch: opt Epoch] ) -> HashSet
= validator_addresses, + ( "liveness_info" ) -> LivenessInfo = liveness_info, + ( "stake" / [validator: Address] / [epoch: opt Epoch] ) -> Option = validator_stake, @@ -249,6 +254,44 @@ where read_all_validator_addresses(ctx.state, epoch) } +/// Get liveness information for all consensus validators in the current epoch. +fn liveness_info( + ctx: RequestCtx<'_, D, H, V, T>, +) -> namada_storage::Result +where + D: 'static + DB + for<'iter> DBIter<'iter> + Sync, + H: 'static + StorageHasher + Sync, +{ + let epoch = ctx.state.in_mem().last_epoch; + let consensus_validators = + read_consensus_validator_set_addresses(ctx.state, epoch)?; + let params = read_pos_params::<_, governance::Store<_>>(ctx.state)?; + + let mut result = Vec::with_capacity(consensus_validators.len()); + for validator in consensus_validators { + if let Some(pubkey) = get_consensus_key::<_, governance::Store<_>>( + ctx.state, &validator, epoch, + )? { + let comet_address = tm_consensus_key_raw_hash(&pubkey); + let sum_liveness_handle = liveness_sum_missed_votes_handle(); + let missed_votes = sum_liveness_handle + .get(ctx.state, &validator)? + .unwrap_or_default(); + result.push(ValidatorLiveness { + native_address: validator, + comet_address, + missed_votes, + }) + }; + } + + Ok(LivenessInfo { + liveness_window_len: params.liveness_window_check, + liveness_threshold: params.liveness_threshold, + validators: result, + }) +} + /// Get the validator commission rate and max commission rate change per epoch fn validator_commission( ctx: RequestCtx<'_, D, H, V, T>, diff --git a/crates/sdk/src/rpc.rs b/crates/sdk/src/rpc.rs index 8958778c35..001cc7a716 100644 --- a/crates/sdk/src/rpc.rs +++ b/crates/sdk/src/rpc.rs @@ -41,7 +41,7 @@ use namada_io::{display_line, edisplay_line, Client, Io}; use namada_parameters::{storage as params_storage, EpochDuration}; use namada_proof_of_stake::parameters::PosParams; use namada_proof_of_stake::types::{ - BondsAndUnbondsDetails, CommissionPair, ValidatorMetaData, + BondsAndUnbondsDetails, CommissionPair, LivenessInfo, ValidatorMetaData, WeightedValidator, }; use namada_state::LastBlock; @@ -754,6 +754,13 @@ pub async fn get_all_validators( ) } +/// Get liveness info for all consensus validators in the current epoch +pub async fn get_validators_liveness_info( + client: &C, +) -> Result { + convert_response::(RPC.vp().pos().liveness_info(client).await) +} + /// Get all consensus validators in the given epoch pub async fn get_all_consensus_validators( client: &C,