From 35624bbc7f92eaf9ff4ac10184d1fd0cec0e50a3 Mon Sep 17 00:00:00 2001 From: plaidfinch Date: Wed, 22 May 2024 22:17:46 +0000 Subject: [PATCH] Thread validator change events through code --- .../validator_handler/uptime_tracker.rs | 7 ++++ .../validator_handler/validator_manager.rs | 24 +++++++++--- .../validator_handler/validator_store.rs | 12 +++++- crates/core/component/stake/src/event.rs | 25 ++++++++++-- .../gen/penumbra.core.component.stake.v1.rs | 36 +++++++++--------- .../penumbra.core.component.stake.v1.serde.rs | 24 ++++++------ .../proto/src/gen/proto_descriptor.bin.no_lfs | Bin 533712 -> 533700 bytes .../core/component/stake/v1/stake.proto | 16 ++++---- 8 files changed, 94 insertions(+), 50 deletions(-) diff --git a/crates/core/component/stake/src/component/validator_handler/uptime_tracker.rs b/crates/core/component/stake/src/component/validator_handler/uptime_tracker.rs index bb6f68e9b1..c22e77abe9 100644 --- a/crates/core/component/stake/src/component/validator_handler/uptime_tracker.rs +++ b/crates/core/component/stake/src/component/validator_handler/uptime_tracker.rs @@ -9,6 +9,7 @@ use { }, StateReadExt as _, }, + event, params::StakeParameters, validator, IdentityKey, Uptime, }, @@ -16,6 +17,7 @@ use { async_trait::async_trait, cnidarium::StateWrite, futures::StreamExt as _, + penumbra_proto::StateWriteProto, penumbra_sct::component::clock::EpochRead, std::collections::BTreeMap, tap::Tap, @@ -174,6 +176,11 @@ pub trait ValidatorUptimeTracker: StateWrite { metrics::gauge!(metrics::MISSED_BLOCKS, "identity_key" => identity_key.to_string()) .set(uptime.num_missed_blocks() as f64); + if !voted { + // If the validator didn't sign, we need to emit a missed block event. + self.record_proto(event::validator_missed_block(identity_key)); + } + uptime.mark_height_as_signed(height, voted)?; if uptime.num_missed_blocks() as u64 >= params.missed_blocks_maximum { self.set_validator_state(&identity_key, validator::State::Jailed) diff --git a/crates/core/component/stake/src/component/validator_handler/validator_manager.rs b/crates/core/component/stake/src/component/validator_handler/validator_manager.rs index e20f8ae68e..2fa17fc577 100644 --- a/crates/core/component/stake/src/component/validator_handler/validator_manager.rs +++ b/crates/core/component/stake/src/component/validator_handler/validator_manager.rs @@ -70,16 +70,16 @@ use { /// │ ┌─────────────┐ ┌────────────┐ │ │◀──┘ │ /// └──▶│ Jailed │◀───────│ Active │◀───▶│ Inactive │ │ /// └─────────────┘ └────────────┘ │ │◀────┘ -/// │ └──────────────┘ -/// │ ▲ -/// └──────────────────────────────────────────┘ -/// +/// │ └──────────────┘ +/// │ ▲ +/// └──────────────────────────────────────────┘ +/// /// ╔═════════════════╗ /// ║ starting state ║ /// ╚═════════════════╝ /// ┏━━━━━━━━━━━━━━━━━┓ /// ┃ terminal state ┃ -/// ┗━━━━━━━━━━━━━━━━━┛ +/// ┗━━━━━━━━━━━━━━━━━┛ /// ``` /// /// [`add_validator`]: Self::add_validator @@ -302,6 +302,8 @@ pub trait ValidatorManager: StateWrite { tracing::info!("successful state transition"); self.put(validator_state_path, new_state); + self.record_proto(event::validator_state_change(*identity_key, new_state)); + Ok((old_state, new_state)) } @@ -477,6 +479,10 @@ pub trait ValidatorManager: StateWrite { // ... and its reward rate data in the JMT. self.set_validator_rate_data(&validator_identity, initial_rate_data); + // Track the validator's definition in an event (the rest of the attributes will be tracked + // in events emitted by the calls to set_* methods below). + self.record_proto(event::validator_definition_upload(validator.clone())); + // We initialize the validator's state, power, and bonding state. self.set_initial_validator_state(&validator_identity, initial_state)?; self.set_validator_power(&validator_identity, initial_voting_power)?; @@ -587,7 +593,13 @@ pub trait ValidatorManager: StateWrite { // consensus key. self.register_consensus_key(&validator.identity_key, &validator.consensus_key); - self.put(state_key::validators::definitions::by_id(id), validator); + self.put( + state_key::validators::definitions::by_id(id), + validator.clone(), + ); + + // Track the validator's definition in an event. + self.record_proto(event::validator_definition_upload(validator)); Ok(()) } diff --git a/crates/core/component/stake/src/component/validator_handler/validator_store.rs b/crates/core/component/stake/src/component/validator_handler/validator_store.rs index 151571d47b..01e6e00bb4 100644 --- a/crates/core/component/stake/src/component/validator_handler/validator_store.rs +++ b/crates/core/component/stake/src/component/validator_handler/validator_store.rs @@ -1,5 +1,6 @@ use crate::{ component::{StateReadExt as _, MAX_VOTING_POWER}, + event, rate::RateData, state_key, validator::{self, BondingState::*, State, Validator}, @@ -250,8 +251,9 @@ pub(crate) trait ValidatorDataWrite: StateWrite { tracing::debug!(?state, validator_identity = %identity_key, "set bonding state for validator"); self.put( state_key::validators::pool::bonding_state::by_id(identity_key), - state, + state.clone(), ); + self.record_proto(event::validator_bonding_state_change(*identity_key, state)); } #[instrument(skip(self))] @@ -268,6 +270,10 @@ pub(crate) trait ValidatorDataWrite: StateWrite { state_key::validators::power::by_id(identity_key), voting_power, ); + self.record_proto(event::validator_voting_power_change( + *identity_key, + voting_power, + )); Ok(()) } @@ -284,6 +290,7 @@ pub(crate) trait ValidatorDataWrite: StateWrite { } self.put(state_key::validators::state::by_id(id), initial_state); + self.record_proto(event::validator_state_change(*id, initial_state)); Ok(()) } @@ -292,8 +299,9 @@ pub(crate) trait ValidatorDataWrite: StateWrite { tracing::debug!("setting validator rate data"); self.put( state_key::validators::rate::current_by_id(identity_key), - rate_data, + rate_data.clone(), ); + self.record_proto(event::validator_rate_data_change(*identity_key, rate_data)); } #[instrument(skip(self))] diff --git a/crates/core/component/stake/src/event.rs b/crates/core/component/stake/src/event.rs index 51da43c5d6..d1c6e5d9f3 100644 --- a/crates/core/component/stake/src/event.rs +++ b/crates/core/component/stake/src/event.rs @@ -1,10 +1,11 @@ use crate::{ - validator::{BondingState, Definition, State}, + rate, + validator::{BondingState, State, Validator}, Delegate, IdentityKey, Undelegate, }; use penumbra_num::Amount; use penumbra_proto::core::component::stake::v1 as pb; -use tendermint::abci::{types::Misbehavior, Event, EventAttributeIndexExt}; +use tendermint::abci::types::Misbehavior; pub fn validator_state_change( identity_key: IdentityKey, @@ -36,9 +37,25 @@ pub fn validator_bonding_state_change( } } -pub fn validator_definition_upload(definition: Definition) -> pb::EventValidatorDefinitionUpload { +pub fn validator_rate_data_change( + identity_key: IdentityKey, + rate_data: rate::RateData, +) -> pb::EventRateDataChange { + pb::EventRateDataChange { + identity_key: Some(identity_key.into()), + rate_data: Some(rate_data.into()), + } +} + +pub fn validator_definition_upload(validator: Validator) -> pb::EventValidatorDefinitionUpload { pb::EventValidatorDefinitionUpload { - definition: Some(definition.into()), + validator: Some(validator.into()), + } +} + +pub fn validator_missed_block(identity_key: IdentityKey) -> pb::EventValidatorMissedBlock { + pb::EventValidatorMissedBlock { + identity_key: Some(identity_key.into()), } } diff --git a/crates/proto/src/gen/penumbra.core.component.stake.v1.rs b/crates/proto/src/gen/penumbra.core.component.stake.v1.rs index e93955c2ee..f70e807e9f 100644 --- a/crates/proto/src/gen/penumbra.core.component.stake.v1.rs +++ b/crates/proto/src/gen/penumbra.core.component.stake.v1.rs @@ -896,10 +896,27 @@ impl ::prost::Name for EventValidatorBondingStateChange { } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] +pub struct EventRateDataChange { + /// The validator's identity key. + #[prost(message, optional, tag = "1")] + pub identity_key: ::core::option::Option, + /// The new rate data. + #[prost(message, optional, tag = "2")] + pub rate_data: ::core::option::Option, +} +impl ::prost::Name for EventRateDataChange { + const NAME: &'static str = "EventRateDataChange"; + const PACKAGE: &'static str = "penumbra.core.component.stake.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!("penumbra.core.component.stake.v1.{}", Self::NAME) + } +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct EventValidatorDefinitionUpload { /// The validator definition. #[prost(message, optional, tag = "1")] - pub definition: ::core::option::Option, + pub validator: ::core::option::Option, } impl ::prost::Name for EventValidatorDefinitionUpload { const NAME: &'static str = "EventValidatorDefinitionUpload"; @@ -960,23 +977,6 @@ impl ::prost::Name for EventUndelegate { ::prost::alloc::format!("penumbra.core.component.stake.v1.{}", Self::NAME) } } -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct EventRateDataChange { - /// The validator's identity key. - #[prost(message, optional, tag = "1")] - pub identity_key: ::core::option::Option, - /// The new rate data. - #[prost(message, optional, tag = "2")] - pub rate_data: ::core::option::Option, -} -impl ::prost::Name for EventRateDataChange { - const NAME: &'static str = "EventRateDataChange"; - const PACKAGE: &'static str = "penumbra.core.component.stake.v1"; - fn full_name() -> ::prost::alloc::string::String { - ::prost::alloc::format!("penumbra.core.component.stake.v1.{}", Self::NAME) - } -} /// Generated client implementations. #[cfg(feature = "rpc")] pub mod query_service_client { diff --git a/crates/proto/src/gen/penumbra.core.component.stake.v1.serde.rs b/crates/proto/src/gen/penumbra.core.component.stake.v1.serde.rs index 72e8efacd6..2719d53cb1 100644 --- a/crates/proto/src/gen/penumbra.core.component.stake.v1.serde.rs +++ b/crates/proto/src/gen/penumbra.core.component.stake.v1.serde.rs @@ -1542,12 +1542,12 @@ impl serde::Serialize for EventValidatorDefinitionUpload { { use serde::ser::SerializeStruct; let mut len = 0; - if self.definition.is_some() { + if self.validator.is_some() { len += 1; } let mut struct_ser = serializer.serialize_struct("penumbra.core.component.stake.v1.EventValidatorDefinitionUpload", len)?; - if let Some(v) = self.definition.as_ref() { - struct_ser.serialize_field("definition", v)?; + if let Some(v) = self.validator.as_ref() { + struct_ser.serialize_field("validator", v)?; } struct_ser.end() } @@ -1559,12 +1559,12 @@ impl<'de> serde::Deserialize<'de> for EventValidatorDefinitionUpload { D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "definition", + "validator", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - Definition, + Validator, __SkipField__, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -1587,7 +1587,7 @@ impl<'de> serde::Deserialize<'de> for EventValidatorDefinitionUpload { E: serde::de::Error, { match value { - "definition" => Ok(GeneratedField::Definition), + "validator" => Ok(GeneratedField::Validator), _ => Ok(GeneratedField::__SkipField__), } } @@ -1607,14 +1607,14 @@ impl<'de> serde::Deserialize<'de> for EventValidatorDefinitionUpload { where V: serde::de::MapAccess<'de>, { - let mut definition__ = None; + let mut validator__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::Definition => { - if definition__.is_some() { - return Err(serde::de::Error::duplicate_field("definition")); + GeneratedField::Validator => { + if validator__.is_some() { + return Err(serde::de::Error::duplicate_field("validator")); } - definition__ = map_.next_value()?; + validator__ = map_.next_value()?; } GeneratedField::__SkipField__ => { let _ = map_.next_value::()?; @@ -1622,7 +1622,7 @@ impl<'de> serde::Deserialize<'de> for EventValidatorDefinitionUpload { } } Ok(EventValidatorDefinitionUpload { - definition: definition__, + validator: validator__, }) } } diff --git a/crates/proto/src/gen/proto_descriptor.bin.no_lfs b/crates/proto/src/gen/proto_descriptor.bin.no_lfs index 8df281a2118f1f1f30d293b279f470a9d785268e..413cfb86fab0886f8d9360e8718b697644947d59 100644 GIT binary patch delta 506 zcmXw#K}y3=5QdYPG=`T-CD=A?+G-1?N!1%z5D^!Gh`6e4dI53o(nvvFlq>{aL6?FX z!Htj$6rnp$;1S%2w3OC5FHIK%`RD!rH#7MhtoH}&R~`8sN>jNPu1(>x#9xc}0+n;# zv#`t`H9QPc1$>gj(-L0nSR;^`&QP&sL-_&CV%tnnY>*7)m;W`;eQ2UtsIX<05M87; zY}l+4nJ6*@KJu`5VuP#-1uVA8(WAb)kdz!k)?`TrO4U%uAWh@RRdS9&vzbfKs+c?h z$n&o=tQ?ahENMOBkR;%kQgmnyc2GTXo4|2#k1Fak#YLB z`v`LgJ(1JL%=}1_#PpGl6sx+1+Ck|qX1e6;pqA#(E%ewv?dt-yZh5(dyZp$*y<|3; zR}=HP{H=v9?|W!kPP8z!3EsucEp|ZRng|D6r<_CIbZLN)ik;#I2Dp-0ND2m+%iq-7 QNTpCEsMN6DUb_GI2gE{GtN;K2 delta 530 zcmYk1O-lk%6o#F1Mw6>jNq*$FrsLIYAwjDa7E#L}3c@c4G7{1@Q7tusS|l!v?xtmn zplvXJAPRT>ftLM%h(ZjpGowO_Gu(IH^FH^zFYQpL9jaYQ&k*QK4{ERv=K<+VWPBL5 z?J%}qIw)7mC*|X6D{~VOwJ=VL$_Fy*);~*q^5MNC?!5S@ZUnHh9Q^@_Ss2QSWS76d ztgr-=Wd#g&`5PeS@rJe!OJYw!hxp85a4E!W%&7UOEzCukm4O_$o4R259g*Th>Vi4U z7KjwrNIkK#i-}D-n@9jSbO=e>?13{fmc;BqOwpO#Zt8>K0*CE5ZT7+J(3xCg4nQ0g zkS~%T=wJ*bNvi{Jave&NpaYmsu{CPLfy}yi&trzunSynM$PL~0 diff --git a/proto/penumbra/penumbra/core/component/stake/v1/stake.proto b/proto/penumbra/penumbra/core/component/stake/v1/stake.proto index f95671e0b1..660da86b09 100644 --- a/proto/penumbra/penumbra/core/component/stake/v1/stake.proto +++ b/proto/penumbra/penumbra/core/component/stake/v1/stake.proto @@ -369,9 +369,16 @@ message EventValidatorBondingStateChange { BondingState bonding_state = 2; } +message EventRateDataChange { + // The validator's identity key. + keys.v1.IdentityKey identity_key = 1; + // The new rate data. + RateData rate_data = 2; +} + message EventValidatorDefinitionUpload { // The validator definition. - ValidatorDefinition definition = 1; + Validator validator = 1; } message EventValidatorMissedBlock { @@ -391,11 +398,4 @@ message EventUndelegate { keys.v1.IdentityKey validator_identity = 1; // The amount of stake undelegated, in the staking token. num.v1.Amount amount = 3; -} - -message EventRateDataChange { - // The validator's identity key. - keys.v1.IdentityKey identity_key = 1; - // The new rate data. - RateData rate_data = 2; } \ No newline at end of file