From cd5112d5e948a7f914c7f5513d727dfef5211a2c Mon Sep 17 00:00:00 2001 From: Jeongseup Date: Tue, 8 Oct 2024 00:40:49 +0900 Subject: [PATCH 1/5] Add new rpc query function to check validators' liveness(uptime) in this epoch --- crates/proof_of_stake/src/storage.rs | 23 ++++++++++++ crates/sdk/src/queries/vp/pos.rs | 54 +++++++++++++++++++++++----- crates/sdk/src/rpc.rs | 7 ++++ 3 files changed, 76 insertions(+), 8 deletions(-) diff --git a/crates/proof_of_stake/src/storage.rs b/crates/proof_of_stake/src/storage.rs index a3d0a61cea..29882c8668 100644 --- a/crates/proof_of_stake/src/storage.rs +++ b/crates/proof_of_stake/src/storage.rs @@ -710,6 +710,29 @@ where .collect() } +/// Read all validator addresses from the consensus set +pub fn read_active_validator_addresses( + storage: &S, + epoch: namada_core::chain::Epoch, +) -> Result> +where + S: StorageRead, + Gov: governance::Read, +{ + let params = read_pos_params::(storage)?; + Ok(validator_addresses_handle() + .at(&epoch) + .iter(storage)? + .map(Result::unwrap) + .filter(|address| { + matches!( + validator_state_handle(address).get(storage, epoch, ¶ms), + Ok(Some(ValidatorState::Consensus)) + ) + }) + .collect()) +} + /// Update PoS total deltas. /// Note: for EpochedDelta, write the value to change storage by pub fn update_total_deltas( diff --git a/crates/sdk/src/queries/vp/pos.rs b/crates/sdk/src/queries/vp/pos.rs index 73aad5ae90..24856e4716 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,7 +17,8 @@ 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_active_validator_addresses, read_all_validator_addresses, read_below_capacity_validator_set_addresses_with_stake, read_consensus_validator_set_addresses_with_stake, read_pos_params, read_total_active_stake, read_total_stake, read_validator_avatar, @@ -51,6 +52,8 @@ router! {POS, ( "addresses" / [epoch: opt Epoch] ) -> HashSet
= validator_addresses, + ( "livenesses" ) -> Vec<(Address, String, u64)> = validator_livenesses, + ( "stake" / [validator: Address] / [epoch: opt Epoch] ) -> Option = validator_stake, @@ -249,6 +252,43 @@ where read_all_validator_addresses(ctx.state, epoch) } +/// Get all the validator livenesses. +fn validator_livenesses( + 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 mut result = vec![]; + let active_validator_set = read_active_validator_addresses::< + _, + governance::Store<_>, + >(ctx.state, epoch)?; + for validator_address in active_validator_set.iter() { + if let Some(pubkey) = get_consensus_key::<_, governance::Store<_>>( + ctx.state, + validator_address, + epoch, + )? { + let tendermint_address = tm_consensus_key_raw_hash(&pubkey); + let sum_liveness_handle = liveness_sum_missed_votes_handle(); + if let Some(missed_counter) = + sum_liveness_handle.get(ctx.state, validator_address)? + { + result.push(( + validator_address.to_owned(), + tendermint_address, + missed_counter, + )) + } + }; + } + + Ok(result) +} + /// Get the validator commission rate and max commission rate change per epoch fn validator_commission( ctx: RequestCtx<'_, D, H, V, T>, @@ -839,11 +879,9 @@ mod test { }; let result = POS.handle(ctx, &request); assert!(result.is_err()); - assert!( - result - .unwrap_err() - .to_string() - .contains("Invalid Tendermint address") - ) + assert!(result + .unwrap_err() + .to_string() + .contains("Invalid Tendermint address")) } } diff --git a/crates/sdk/src/rpc.rs b/crates/sdk/src/rpc.rs index 8958778c35..bbca3e168c 100644 --- a/crates/sdk/src/rpc.rs +++ b/crates/sdk/src/rpc.rs @@ -754,6 +754,13 @@ pub async fn get_all_validators( ) } +/// Get validators livenesses in the given epoch +pub async fn get_validators_livenesses( + client: &C, +) -> Result, error::Error> { + convert_response::(RPC.vp().pos().validator_livenesses(client).await) +} + /// Get all consensus validators in the given epoch pub async fn get_all_consensus_validators( client: &C, From f0464bccd825867e4d0475754b712b0cddbbd475 Mon Sep 17 00:00:00 2001 From: brentstone Date: Mon, 7 Oct 2024 18:36:12 -0700 Subject: [PATCH 2/5] fix up liveness queries --- crates/proof_of_stake/src/storage.rs | 23 ---------- crates/proof_of_stake/src/types/mod.rs | 22 ++++++++++ crates/sdk/src/queries/vp/pos.rs | 60 ++++++++++++++------------ crates/sdk/src/rpc.rs | 10 ++--- 4 files changed, 60 insertions(+), 55 deletions(-) diff --git a/crates/proof_of_stake/src/storage.rs b/crates/proof_of_stake/src/storage.rs index 29882c8668..a3d0a61cea 100644 --- a/crates/proof_of_stake/src/storage.rs +++ b/crates/proof_of_stake/src/storage.rs @@ -710,29 +710,6 @@ where .collect() } -/// Read all validator addresses from the consensus set -pub fn read_active_validator_addresses( - storage: &S, - epoch: namada_core::chain::Epoch, -) -> Result> -where - S: StorageRead, - Gov: governance::Read, -{ - let params = read_pos_params::(storage)?; - Ok(validator_addresses_handle() - .at(&epoch) - .iter(storage)? - .map(Result::unwrap) - .filter(|address| { - matches!( - validator_state_handle(address).get(storage, epoch, ¶ms), - Ok(Some(ValidatorState::Consensus)) - ) - }) - .collect()) -} - /// Update PoS total deltas. /// Note: for EpochedDelta, write the value to change storage by pub fn update_total_deltas( 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 24856e4716..c92bf54b8a 100644 --- a/crates/sdk/src/queries/vp/pos.rs +++ b/crates/sdk/src/queries/vp/pos.rs @@ -18,8 +18,9 @@ use namada_proof_of_stake::slashing::{ }; use namada_proof_of_stake::storage::{ bond_handle, get_consensus_key, liveness_sum_missed_votes_handle, - read_active_validator_addresses, read_all_validator_addresses, + 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, @@ -32,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}; @@ -52,7 +54,7 @@ router! {POS, ( "addresses" / [epoch: opt Epoch] ) -> HashSet
= validator_addresses, - ( "livenesses" ) -> Vec<(Address, String, u64)> = validator_livenesses, + ( "liveness_info" ) -> LivenessInfo = liveness_info, ( "stake" / [validator: Address] / [epoch: opt Epoch] ) -> Option = validator_stake, @@ -252,41 +254,43 @@ where read_all_validator_addresses(ctx.state, epoch) } -/// Get all the validator livenesses. -fn validator_livenesses( +/// Get liveness information for all consensus validators in the current epoch. +fn liveness_info( ctx: RequestCtx<'_, D, H, V, T>, -) -> namada_storage::Result> +) -> 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![]; - let active_validator_set = read_active_validator_addresses::< - _, - governance::Store<_>, - >(ctx.state, epoch)?; - for validator_address in active_validator_set.iter() { + for validator in consensus_validators { if let Some(pubkey) = get_consensus_key::<_, governance::Store<_>>( - ctx.state, - validator_address, - epoch, + ctx.state, &validator, epoch, )? { - let tendermint_address = tm_consensus_key_raw_hash(&pubkey); + let comet_address = tm_consensus_key_raw_hash(&pubkey); let sum_liveness_handle = liveness_sum_missed_votes_handle(); - if let Some(missed_counter) = - sum_liveness_handle.get(ctx.state, validator_address)? + if let Some(missed_votes) = + sum_liveness_handle.get(ctx.state, &validator)? { - result.push(( - validator_address.to_owned(), - tendermint_address, - missed_counter, - )) + result.push(ValidatorLiveness { + native_address: validator, + comet_address, + missed_votes, + }) } }; } - Ok(result) + 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 @@ -879,9 +883,11 @@ mod test { }; let result = POS.handle(ctx, &request); assert!(result.is_err()); - assert!(result - .unwrap_err() - .to_string() - .contains("Invalid Tendermint address")) + assert!( + result + .unwrap_err() + .to_string() + .contains("Invalid Tendermint address") + ) } } diff --git a/crates/sdk/src/rpc.rs b/crates/sdk/src/rpc.rs index bbca3e168c..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,11 +754,11 @@ pub async fn get_all_validators( ) } -/// Get validators livenesses in the given epoch -pub async fn get_validators_livenesses( +/// Get liveness info for all consensus validators in the current epoch +pub async fn get_validators_liveness_info( client: &C, -) -> Result, error::Error> { - convert_response::(RPC.vp().pos().validator_livenesses(client).await) +) -> Result { + convert_response::(RPC.vp().pos().liveness_info(client).await) } /// Get all consensus validators in the given epoch From bdf352e9a553f6ddb8305a171f7dbe75354c485d Mon Sep 17 00:00:00 2001 From: brentstone Date: Wed, 9 Oct 2024 18:48:04 -0700 Subject: [PATCH 3/5] changelog: add #3899 --- .../unreleased/improvements/3899-get-validators-livenesses.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/improvements/3899-get-validators-livenesses.md 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 From 16e92a8e716feca13113d7956082fd41b65043a2 Mon Sep 17 00:00:00 2001 From: brentstone Date: Thu, 10 Oct 2024 16:07:06 -0700 Subject: [PATCH 4/5] small fix from comments --- crates/sdk/src/queries/vp/pos.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/sdk/src/queries/vp/pos.rs b/crates/sdk/src/queries/vp/pos.rs index c92bf54b8a..5142b4ff52 100644 --- a/crates/sdk/src/queries/vp/pos.rs +++ b/crates/sdk/src/queries/vp/pos.rs @@ -274,15 +274,14 @@ where )? { let comet_address = tm_consensus_key_raw_hash(&pubkey); let sum_liveness_handle = liveness_sum_missed_votes_handle(); - if let Some(missed_votes) = - sum_liveness_handle.get(ctx.state, &validator)? - { - result.push(ValidatorLiveness { - native_address: validator, - comet_address, - missed_votes, - }) - } + let missed_votes = sum_liveness_handle + .get(ctx.state, &validator)? + .unwrap_or_default(); + result.push(ValidatorLiveness { + native_address: validator, + comet_address, + missed_votes, + }) }; } From 6dbf635028a5ce25256f715881703a5872f71559 Mon Sep 17 00:00:00 2001 From: Tomas Zemanovic Date: Fri, 11 Oct 2024 09:47:49 +0200 Subject: [PATCH 5/5] Update crates/sdk/src/queries/vp/pos.rs --- crates/sdk/src/queries/vp/pos.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/sdk/src/queries/vp/pos.rs b/crates/sdk/src/queries/vp/pos.rs index 5142b4ff52..0076419c73 100644 --- a/crates/sdk/src/queries/vp/pos.rs +++ b/crates/sdk/src/queries/vp/pos.rs @@ -267,7 +267,7 @@ where read_consensus_validator_set_addresses(ctx.state, epoch)?; let params = read_pos_params::<_, governance::Store<_>>(ctx.state)?; - let mut result = vec![]; + 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,