diff --git a/crates/core/app/src/action_handler/actions/submit.rs b/crates/core/app/src/action_handler/actions/submit.rs index 8776798166..3d39681999 100644 --- a/crates/core/app/src/action_handler/actions/submit.rs +++ b/crates/core/app/src/action_handler/actions/submit.rs @@ -296,7 +296,7 @@ impl AppActionHandler for ProposalSubmit { // state needed to vote as delegators state.mark_proposal_started(); - state.record_proto(event::proposal_submit(self)); + state.record_proto(event::proposal_submit(self, current_block, voting_end)); tracing::debug!(proposal = %proposal_id, "created proposal"); diff --git a/crates/core/component/governance/src/action_handler/delegator_vote.rs b/crates/core/component/governance/src/action_handler/delegator_vote.rs index 8f284153ba..00194240aa 100644 --- a/crates/core/component/governance/src/action_handler/delegator_vote.rs +++ b/crates/core/component/governance/src/action_handler/delegator_vote.rs @@ -91,7 +91,7 @@ impl ActionHandler for DelegatorVote { .cast_delegator_vote(*proposal, identity_key, *vote, nullifier, *unbonded_amount) .await?; - state.record_proto(event::delegator_vote(self)); + state.record_proto(event::delegator_vote(self, &identity_key)); Ok(()) } diff --git a/crates/core/component/governance/src/action_handler/validator_vote.rs b/crates/core/component/governance/src/action_handler/validator_vote.rs index 33a0dccd24..caed7b3547 100644 --- a/crates/core/component/governance/src/action_handler/validator_vote.rs +++ b/crates/core/component/governance/src/action_handler/validator_vote.rs @@ -114,7 +114,10 @@ impl ActionHandler for ValidatorVote { } } - state.record_proto(event::validator_vote(self)); + let voting_power = state + .specific_validator_voting_power_at_proposal_start(*proposal, *identity_key) + .await?; + state.record_proto(event::validator_vote(self, voting_power)); Ok(()) } diff --git a/crates/core/component/governance/src/component.rs b/crates/core/component/governance/src/component.rs index 8b7b368362..4581dc0d81 100644 --- a/crates/core/component/governance/src/component.rs +++ b/crates/core/component/governance/src/component.rs @@ -144,7 +144,7 @@ pub async fn enact_all_passed_proposals(mut state: S) -> Result<( .ok_or_else(|| { anyhow::anyhow!("proposal {} does not exist", proposal_id) })?; - state.record_proto(event::enact_proposal(&proposal)); + state.record_proto(event::proposal_passed(&proposal)); } tally::Outcome::Fail => { tracing::info!(proposal = %proposal_id, "proposal failed"); diff --git a/crates/core/component/governance/src/component/view.rs b/crates/core/component/governance/src/component/view.rs index d76d9df811..d05a8b5911 100644 --- a/crates/core/component/governance/src/component/view.rs +++ b/crates/core/component/governance/src/component/view.rs @@ -422,6 +422,20 @@ pub trait StateReadExt: StateRead + penumbra_stake::StateReadExt { Ok(()) } + /// Get a specific validator's voting power for a proposal. + async fn specific_validator_voting_power_at_proposal_start( + &self, + proposal_id: u64, + identity_key: IdentityKey, + ) -> Result { + self.get_proto(&state_key::voting_power_at_proposal_start( + proposal_id, + identity_key, + )) + .await + .map(Option::unwrap_or_default) + } + /// Get all the active validator voting power for the proposal. async fn validator_voting_power_at_proposal_start( &self, diff --git a/crates/core/component/governance/src/event.rs b/crates/core/component/governance/src/event.rs index 5737ae405b..699f2bd2b4 100644 --- a/crates/core/component/governance/src/event.rs +++ b/crates/core/component/governance/src/event.rs @@ -1,12 +1,17 @@ use penumbra_proto::penumbra::core::component::governance::v1 as pb; +use penumbra_stake::IdentityKey; use crate::{ DelegatorVote, Proposal, ProposalDepositClaim, ProposalSubmit, ProposalWithdraw, ValidatorVote, }; -pub fn delegator_vote(delegator_vote: &DelegatorVote) -> pb::EventDelegatorVote { +pub fn delegator_vote( + delegator_vote: &DelegatorVote, + validator_identity_key: &IdentityKey, +) -> pb::EventDelegatorVote { pb::EventDelegatorVote { vote: Some(pb::DelegatorVote::from(*delegator_vote)), + validator_identity_key: Some(validator_identity_key.clone().into()), } } @@ -18,9 +23,10 @@ pub fn proposal_deposit_claim( } } -pub fn validator_vote(validator_vote: &ValidatorVote) -> pb::EventValidatorVote { +pub fn validator_vote(validator_vote: &ValidatorVote, voting_power: u64) -> pb::EventValidatorVote { pb::EventValidatorVote { vote: Some(pb::ValidatorVote::from(validator_vote.clone())), + voting_power, } } @@ -30,14 +36,20 @@ pub fn proposal_withdraw(withdraw: &ProposalWithdraw) -> pb::EventProposalWithdr } } -pub fn proposal_submit(submit: &ProposalSubmit) -> pb::EventProposalSubmit { +pub fn proposal_submit( + submit: &ProposalSubmit, + start_height: u64, + end_height: u64, +) -> pb::EventProposalSubmit { pb::EventProposalSubmit { submit: Some(pb::ProposalSubmit::from(submit.clone())), + start_height, + end_height, } } -pub fn enact_proposal(proposal: &Proposal) -> pb::EventEnactProposal { - pb::EventEnactProposal { +pub fn proposal_passed(proposal: &Proposal) -> pb::EventProposalPassed { + pb::EventProposalPassed { proposal: Some(pb::Proposal::from(proposal.clone())), } } diff --git a/crates/core/component/governance/src/proposal.rs b/crates/core/component/governance/src/proposal.rs index 1f7b3bb2bb..050bc21fa6 100644 --- a/crates/core/component/governance/src/proposal.rs +++ b/crates/core/component/governance/src/proposal.rs @@ -201,8 +201,9 @@ impl TryFrom for Proposal { } /// The specific kind of a proposal. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[cfg_attr(feature = "clap", derive(clap::Subcommand))] +#[serde(try_from = "pb::ProposalKind", into = "pb::ProposalKind")] pub enum ProposalKind { /// A signaling proposal. #[cfg_attr(feature = "clap", clap(display_order = 100))] @@ -227,6 +228,38 @@ pub enum ProposalKind { UnfreezeIbcClient, } +impl From for pb::ProposalKind { + fn from(kind: ProposalKind) -> pb::ProposalKind { + match kind { + ProposalKind::Signaling => pb::ProposalKind::Signaling, + ProposalKind::Emergency => pb::ProposalKind::Emergency, + ProposalKind::ParameterChange => pb::ProposalKind::ParameterChange, + ProposalKind::CommunityPoolSpend => pb::ProposalKind::CommunityPoolSpend, + ProposalKind::UpgradePlan => pb::ProposalKind::UpgradePlan, + ProposalKind::FreezeIbcClient => pb::ProposalKind::FreezeIbcClient, + ProposalKind::UnfreezeIbcClient => pb::ProposalKind::UnfreezeIbcClient, + } + } +} + +impl TryFrom for ProposalKind { + type Error = anyhow::Error; + + fn try_from(kind: pb::ProposalKind) -> anyhow::Result { + let kind = match kind { + pb::ProposalKind::Unspecified => anyhow::bail!("unspecified proposal kind"), + pb::ProposalKind::Signaling => ProposalKind::Signaling, + pb::ProposalKind::Emergency => ProposalKind::Emergency, + pb::ProposalKind::ParameterChange => ProposalKind::ParameterChange, + pb::ProposalKind::CommunityPoolSpend => ProposalKind::CommunityPoolSpend, + pb::ProposalKind::UpgradePlan => ProposalKind::UpgradePlan, + pb::ProposalKind::FreezeIbcClient => ProposalKind::FreezeIbcClient, + pb::ProposalKind::UnfreezeIbcClient => ProposalKind::UnfreezeIbcClient, + }; + Ok(kind) + } +} + impl FromStr for ProposalKind { type Err = anyhow::Error; diff --git a/crates/proto/src/gen/penumbra.core.component.governance.v1.rs b/crates/proto/src/gen/penumbra.core.component.governance.v1.rs index 6da0dcebf1..4c188cf312 100644 --- a/crates/proto/src/gen/penumbra.core.component.governance.v1.rs +++ b/crates/proto/src/gen/penumbra.core.component.governance.v1.rs @@ -1224,6 +1224,11 @@ pub struct EventDelegatorVote { /// The delegator vote. #[prost(message, optional, tag = "1")] pub vote: ::core::option::Option, + /// The corresponding validator's identity key. + #[prost(message, optional, tag = "2")] + pub validator_identity_key: ::core::option::Option< + super::super::super::keys::v1::IdentityKey, + >, } impl ::prost::Name for EventDelegatorVote { const NAME: &'static str = "EventDelegatorVote"; @@ -1252,6 +1257,9 @@ pub struct EventValidatorVote { /// The validator vote. #[prost(message, optional, tag = "1")] pub vote: ::core::option::Option, + /// The validator's voting power at the time of the proposal's start. + #[prost(uint64, tag = "2")] + pub voting_power: u64, } impl ::prost::Name for EventValidatorVote { const NAME: &'static str = "EventValidatorVote"; @@ -1280,6 +1288,12 @@ pub struct EventProposalSubmit { /// Details on the submitted proposal. #[prost(message, optional, tag = "1")] pub submit: ::core::option::Option, + /// The start height for the proposal. + #[prost(uint64, tag = "2")] + pub start_height: u64, + /// The end height for the proposal. + #[prost(uint64, tag = "3")] + pub end_height: u64, } impl ::prost::Name for EventProposalSubmit { const NAME: &'static str = "EventProposalSubmit"; @@ -1290,13 +1304,13 @@ impl ::prost::Name for EventProposalSubmit { } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct EventEnactProposal { - /// The enacted proposal. +pub struct EventProposalPassed { + /// The passed proposal. #[prost(message, optional, tag = "1")] pub proposal: ::core::option::Option, } -impl ::prost::Name for EventEnactProposal { - const NAME: &'static str = "EventEnactProposal"; +impl ::prost::Name for EventProposalPassed { + const NAME: &'static str = "EventProposalPassed"; const PACKAGE: &'static str = "penumbra.core.component.governance.v1"; fn full_name() -> ::prost::alloc::string::String { ::prost::alloc::format!("penumbra.core.component.governance.v1.{}", Self::NAME) @@ -1330,6 +1344,52 @@ impl ::prost::Name for EventProposalSlashed { ::prost::alloc::format!("penumbra.core.component.governance.v1.{}", Self::NAME) } } +/// All the different kinds of proposals. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ProposalKind { + /// To make the linter happy + Unspecified = 0, + Signaling = 1, + Emergency = 2, + ParameterChange = 3, + CommunityPoolSpend = 4, + UpgradePlan = 5, + FreezeIbcClient = 6, + UnfreezeIbcClient = 7, +} +impl ProposalKind { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + ProposalKind::Unspecified => "PROPOSAL_KIND_UNSPECIFIED", + ProposalKind::Signaling => "PROPOSAL_KIND_SIGNALING", + ProposalKind::Emergency => "PROPOSAL_KIND_EMERGENCY", + ProposalKind::ParameterChange => "PROPOSAL_KIND_PARAMETER_CHANGE", + ProposalKind::CommunityPoolSpend => "PROPOSAL_KIND_COMMUNITY_POOL_SPEND", + ProposalKind::UpgradePlan => "PROPOSAL_KIND_UPGRADE_PLAN", + ProposalKind::FreezeIbcClient => "PROPOSAL_KIND_FREEZE_IBC_CLIENT", + ProposalKind::UnfreezeIbcClient => "PROPOSAL_KIND_UNFREEZE_IBC_CLIENT", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "PROPOSAL_KIND_UNSPECIFIED" => Some(Self::Unspecified), + "PROPOSAL_KIND_SIGNALING" => Some(Self::Signaling), + "PROPOSAL_KIND_EMERGENCY" => Some(Self::Emergency), + "PROPOSAL_KIND_PARAMETER_CHANGE" => Some(Self::ParameterChange), + "PROPOSAL_KIND_COMMUNITY_POOL_SPEND" => Some(Self::CommunityPoolSpend), + "PROPOSAL_KIND_UPGRADE_PLAN" => Some(Self::UpgradePlan), + "PROPOSAL_KIND_FREEZE_IBC_CLIENT" => Some(Self::FreezeIbcClient), + "PROPOSAL_KIND_UNFREEZE_IBC_CLIENT" => Some(Self::UnfreezeIbcClient), + _ => None, + } + } +} /// Generated client implementations. #[cfg(feature = "rpc")] pub mod query_service_client { diff --git a/crates/proto/src/gen/penumbra.core.component.governance.v1.serde.rs b/crates/proto/src/gen/penumbra.core.component.governance.v1.serde.rs index 14e37c1c1c..e5a2731256 100644 --- a/crates/proto/src/gen/penumbra.core.component.governance.v1.serde.rs +++ b/crates/proto/src/gen/penumbra.core.component.governance.v1.serde.rs @@ -1952,10 +1952,16 @@ impl serde::Serialize for EventDelegatorVote { if self.vote.is_some() { len += 1; } + if self.validator_identity_key.is_some() { + len += 1; + } let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1.EventDelegatorVote", len)?; if let Some(v) = self.vote.as_ref() { struct_ser.serialize_field("vote", v)?; } + if let Some(v) = self.validator_identity_key.as_ref() { + struct_ser.serialize_field("validatorIdentityKey", v)?; + } struct_ser.end() } } @@ -1967,11 +1973,14 @@ impl<'de> serde::Deserialize<'de> for EventDelegatorVote { { const FIELDS: &[&str] = &[ "vote", + "validator_identity_key", + "validatorIdentityKey", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { Vote, + ValidatorIdentityKey, __SkipField__, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -1995,6 +2004,7 @@ impl<'de> serde::Deserialize<'de> for EventDelegatorVote { { match value { "vote" => Ok(GeneratedField::Vote), + "validatorIdentityKey" | "validator_identity_key" => Ok(GeneratedField::ValidatorIdentityKey), _ => Ok(GeneratedField::__SkipField__), } } @@ -2015,6 +2025,7 @@ impl<'de> serde::Deserialize<'de> for EventDelegatorVote { V: serde::de::MapAccess<'de>, { let mut vote__ = None; + let mut validator_identity_key__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Vote => { @@ -2023,6 +2034,12 @@ impl<'de> serde::Deserialize<'de> for EventDelegatorVote { } vote__ = map_.next_value()?; } + GeneratedField::ValidatorIdentityKey => { + if validator_identity_key__.is_some() { + return Err(serde::de::Error::duplicate_field("validatorIdentityKey")); + } + validator_identity_key__ = map_.next_value()?; + } GeneratedField::__SkipField__ => { let _ = map_.next_value::()?; } @@ -2030,13 +2047,14 @@ impl<'de> serde::Deserialize<'de> for EventDelegatorVote { } Ok(EventDelegatorVote { vote: vote__, + validator_identity_key: validator_identity_key__, }) } } deserializer.deserialize_struct("penumbra.core.component.governance.v1.EventDelegatorVote", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for EventEnactProposal { +impl serde::Serialize for EventProposalDepositClaim { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -2044,29 +2062,30 @@ impl serde::Serialize for EventEnactProposal { { use serde::ser::SerializeStruct; let mut len = 0; - if self.proposal.is_some() { + if self.deposit_claim.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1.EventEnactProposal", len)?; - if let Some(v) = self.proposal.as_ref() { - struct_ser.serialize_field("proposal", v)?; + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1.EventProposalDepositClaim", len)?; + if let Some(v) = self.deposit_claim.as_ref() { + struct_ser.serialize_field("depositClaim", v)?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for EventEnactProposal { +impl<'de> serde::Deserialize<'de> for EventProposalDepositClaim { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "proposal", + "deposit_claim", + "depositClaim", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - Proposal, + DepositClaim, __SkipField__, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -2089,7 +2108,7 @@ impl<'de> serde::Deserialize<'de> for EventEnactProposal { E: serde::de::Error, { match value { - "proposal" => Ok(GeneratedField::Proposal), + "depositClaim" | "deposit_claim" => Ok(GeneratedField::DepositClaim), _ => Ok(GeneratedField::__SkipField__), } } @@ -2099,39 +2118,39 @@ impl<'de> serde::Deserialize<'de> for EventEnactProposal { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = EventEnactProposal; + type Value = EventProposalDepositClaim; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct penumbra.core.component.governance.v1.EventEnactProposal") + formatter.write_str("struct penumbra.core.component.governance.v1.EventProposalDepositClaim") } - fn visit_map(self, mut map_: V) -> std::result::Result + fn visit_map(self, mut map_: V) -> std::result::Result where V: serde::de::MapAccess<'de>, { - let mut proposal__ = None; + let mut deposit_claim__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::Proposal => { - if proposal__.is_some() { - return Err(serde::de::Error::duplicate_field("proposal")); + GeneratedField::DepositClaim => { + if deposit_claim__.is_some() { + return Err(serde::de::Error::duplicate_field("depositClaim")); } - proposal__ = map_.next_value()?; + deposit_claim__ = map_.next_value()?; } GeneratedField::__SkipField__ => { let _ = map_.next_value::()?; } } } - Ok(EventEnactProposal { - proposal: proposal__, + Ok(EventProposalDepositClaim { + deposit_claim: deposit_claim__, }) } } - deserializer.deserialize_struct("penumbra.core.component.governance.v1.EventEnactProposal", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("penumbra.core.component.governance.v1.EventProposalDepositClaim", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for EventProposalDepositClaim { +impl serde::Serialize for EventProposalFailed { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -2139,30 +2158,29 @@ impl serde::Serialize for EventProposalDepositClaim { { use serde::ser::SerializeStruct; let mut len = 0; - if self.deposit_claim.is_some() { + if self.proposal.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1.EventProposalDepositClaim", len)?; - if let Some(v) = self.deposit_claim.as_ref() { - struct_ser.serialize_field("depositClaim", v)?; + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1.EventProposalFailed", len)?; + if let Some(v) = self.proposal.as_ref() { + struct_ser.serialize_field("proposal", v)?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for EventProposalDepositClaim { +impl<'de> serde::Deserialize<'de> for EventProposalFailed { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "deposit_claim", - "depositClaim", + "proposal", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - DepositClaim, + Proposal, __SkipField__, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -2185,7 +2203,7 @@ impl<'de> serde::Deserialize<'de> for EventProposalDepositClaim { E: serde::de::Error, { match value { - "depositClaim" | "deposit_claim" => Ok(GeneratedField::DepositClaim), + "proposal" => Ok(GeneratedField::Proposal), _ => Ok(GeneratedField::__SkipField__), } } @@ -2195,39 +2213,39 @@ impl<'de> serde::Deserialize<'de> for EventProposalDepositClaim { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = EventProposalDepositClaim; + type Value = EventProposalFailed; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct penumbra.core.component.governance.v1.EventProposalDepositClaim") + formatter.write_str("struct penumbra.core.component.governance.v1.EventProposalFailed") } - fn visit_map(self, mut map_: V) -> std::result::Result + fn visit_map(self, mut map_: V) -> std::result::Result where V: serde::de::MapAccess<'de>, { - let mut deposit_claim__ = None; + let mut proposal__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::DepositClaim => { - if deposit_claim__.is_some() { - return Err(serde::de::Error::duplicate_field("depositClaim")); + GeneratedField::Proposal => { + if proposal__.is_some() { + return Err(serde::de::Error::duplicate_field("proposal")); } - deposit_claim__ = map_.next_value()?; + proposal__ = map_.next_value()?; } GeneratedField::__SkipField__ => { let _ = map_.next_value::()?; } } } - Ok(EventProposalDepositClaim { - deposit_claim: deposit_claim__, + Ok(EventProposalFailed { + proposal: proposal__, }) } } - deserializer.deserialize_struct("penumbra.core.component.governance.v1.EventProposalDepositClaim", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("penumbra.core.component.governance.v1.EventProposalFailed", FIELDS, GeneratedVisitor) } } -impl serde::Serialize for EventProposalFailed { +impl serde::Serialize for EventProposalPassed { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result where @@ -2238,14 +2256,14 @@ impl serde::Serialize for EventProposalFailed { if self.proposal.is_some() { len += 1; } - let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1.EventProposalFailed", len)?; + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1.EventProposalPassed", len)?; if let Some(v) = self.proposal.as_ref() { struct_ser.serialize_field("proposal", v)?; } struct_ser.end() } } -impl<'de> serde::Deserialize<'de> for EventProposalFailed { +impl<'de> serde::Deserialize<'de> for EventProposalPassed { #[allow(deprecated)] fn deserialize(deserializer: D) -> std::result::Result where @@ -2290,13 +2308,13 @@ impl<'de> serde::Deserialize<'de> for EventProposalFailed { } struct GeneratedVisitor; impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { - type Value = EventProposalFailed; + type Value = EventProposalPassed; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - formatter.write_str("struct penumbra.core.component.governance.v1.EventProposalFailed") + formatter.write_str("struct penumbra.core.component.governance.v1.EventProposalPassed") } - fn visit_map(self, mut map_: V) -> std::result::Result + fn visit_map(self, mut map_: V) -> std::result::Result where V: serde::de::MapAccess<'de>, { @@ -2314,12 +2332,12 @@ impl<'de> serde::Deserialize<'de> for EventProposalFailed { } } } - Ok(EventProposalFailed { + Ok(EventProposalPassed { proposal: proposal__, }) } } - deserializer.deserialize_struct("penumbra.core.component.governance.v1.EventProposalFailed", FIELDS, GeneratedVisitor) + deserializer.deserialize_struct("penumbra.core.component.governance.v1.EventProposalPassed", FIELDS, GeneratedVisitor) } } impl serde::Serialize for EventProposalSlashed { @@ -2428,10 +2446,24 @@ impl serde::Serialize for EventProposalSubmit { if self.submit.is_some() { len += 1; } + if self.start_height != 0 { + len += 1; + } + if self.end_height != 0 { + len += 1; + } let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1.EventProposalSubmit", len)?; if let Some(v) = self.submit.as_ref() { struct_ser.serialize_field("submit", v)?; } + if self.start_height != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("startHeight", ToString::to_string(&self.start_height).as_str())?; + } + if self.end_height != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("endHeight", ToString::to_string(&self.end_height).as_str())?; + } struct_ser.end() } } @@ -2443,11 +2475,17 @@ impl<'de> serde::Deserialize<'de> for EventProposalSubmit { { const FIELDS: &[&str] = &[ "submit", + "start_height", + "startHeight", + "end_height", + "endHeight", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { Submit, + StartHeight, + EndHeight, __SkipField__, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -2471,6 +2509,8 @@ impl<'de> serde::Deserialize<'de> for EventProposalSubmit { { match value { "submit" => Ok(GeneratedField::Submit), + "startHeight" | "start_height" => Ok(GeneratedField::StartHeight), + "endHeight" | "end_height" => Ok(GeneratedField::EndHeight), _ => Ok(GeneratedField::__SkipField__), } } @@ -2491,6 +2531,8 @@ impl<'de> serde::Deserialize<'de> for EventProposalSubmit { V: serde::de::MapAccess<'de>, { let mut submit__ = None; + let mut start_height__ = None; + let mut end_height__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Submit => { @@ -2499,6 +2541,22 @@ impl<'de> serde::Deserialize<'de> for EventProposalSubmit { } submit__ = map_.next_value()?; } + GeneratedField::StartHeight => { + if start_height__.is_some() { + return Err(serde::de::Error::duplicate_field("startHeight")); + } + start_height__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::EndHeight => { + if end_height__.is_some() { + return Err(serde::de::Error::duplicate_field("endHeight")); + } + end_height__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } GeneratedField::__SkipField__ => { let _ = map_.next_value::()?; } @@ -2506,6 +2564,8 @@ impl<'de> serde::Deserialize<'de> for EventProposalSubmit { } Ok(EventProposalSubmit { submit: submit__, + start_height: start_height__.unwrap_or_default(), + end_height: end_height__.unwrap_or_default(), }) } } @@ -2618,10 +2678,17 @@ impl serde::Serialize for EventValidatorVote { if self.vote.is_some() { len += 1; } + if self.voting_power != 0 { + len += 1; + } let mut struct_ser = serializer.serialize_struct("penumbra.core.component.governance.v1.EventValidatorVote", len)?; if let Some(v) = self.vote.as_ref() { struct_ser.serialize_field("vote", v)?; } + if self.voting_power != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("votingPower", ToString::to_string(&self.voting_power).as_str())?; + } struct_ser.end() } } @@ -2633,11 +2700,14 @@ impl<'de> serde::Deserialize<'de> for EventValidatorVote { { const FIELDS: &[&str] = &[ "vote", + "voting_power", + "votingPower", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { Vote, + VotingPower, __SkipField__, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -2661,6 +2731,7 @@ impl<'de> serde::Deserialize<'de> for EventValidatorVote { { match value { "vote" => Ok(GeneratedField::Vote), + "votingPower" | "voting_power" => Ok(GeneratedField::VotingPower), _ => Ok(GeneratedField::__SkipField__), } } @@ -2681,6 +2752,7 @@ impl<'de> serde::Deserialize<'de> for EventValidatorVote { V: serde::de::MapAccess<'de>, { let mut vote__ = None; + let mut voting_power__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Vote => { @@ -2689,6 +2761,14 @@ impl<'de> serde::Deserialize<'de> for EventValidatorVote { } vote__ = map_.next_value()?; } + GeneratedField::VotingPower => { + if voting_power__.is_some() { + return Err(serde::de::Error::duplicate_field("votingPower")); + } + voting_power__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } GeneratedField::__SkipField__ => { let _ = map_.next_value::()?; } @@ -2696,6 +2776,7 @@ impl<'de> serde::Deserialize<'de> for EventValidatorVote { } Ok(EventValidatorVote { vote: vote__, + voting_power: voting_power__.unwrap_or_default(), }) } } @@ -4746,6 +4827,95 @@ impl<'de> serde::Deserialize<'de> for ProposalInfoResponse { deserializer.deserialize_struct("penumbra.core.component.governance.v1.ProposalInfoResponse", FIELDS, GeneratedVisitor) } } +impl serde::Serialize for ProposalKind { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + let variant = match self { + Self::Unspecified => "PROPOSAL_KIND_UNSPECIFIED", + Self::Signaling => "PROPOSAL_KIND_SIGNALING", + Self::Emergency => "PROPOSAL_KIND_EMERGENCY", + Self::ParameterChange => "PROPOSAL_KIND_PARAMETER_CHANGE", + Self::CommunityPoolSpend => "PROPOSAL_KIND_COMMUNITY_POOL_SPEND", + Self::UpgradePlan => "PROPOSAL_KIND_UPGRADE_PLAN", + Self::FreezeIbcClient => "PROPOSAL_KIND_FREEZE_IBC_CLIENT", + Self::UnfreezeIbcClient => "PROPOSAL_KIND_UNFREEZE_IBC_CLIENT", + }; + serializer.serialize_str(variant) + } +} +impl<'de> serde::Deserialize<'de> for ProposalKind { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "PROPOSAL_KIND_UNSPECIFIED", + "PROPOSAL_KIND_SIGNALING", + "PROPOSAL_KIND_EMERGENCY", + "PROPOSAL_KIND_PARAMETER_CHANGE", + "PROPOSAL_KIND_COMMUNITY_POOL_SPEND", + "PROPOSAL_KIND_UPGRADE_PLAN", + "PROPOSAL_KIND_FREEZE_IBC_CLIENT", + "PROPOSAL_KIND_UNFREEZE_IBC_CLIENT", + ]; + + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = ProposalKind; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + fn visit_i64(self, v: i64) -> std::result::Result + where + E: serde::de::Error, + { + i32::try_from(v) + .ok() + .and_then(|x| x.try_into().ok()) + .ok_or_else(|| { + serde::de::Error::invalid_value(serde::de::Unexpected::Signed(v), &self) + }) + } + + fn visit_u64(self, v: u64) -> std::result::Result + where + E: serde::de::Error, + { + i32::try_from(v) + .ok() + .and_then(|x| x.try_into().ok()) + .ok_or_else(|| { + serde::de::Error::invalid_value(serde::de::Unexpected::Unsigned(v), &self) + }) + } + + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "PROPOSAL_KIND_UNSPECIFIED" => Ok(ProposalKind::Unspecified), + "PROPOSAL_KIND_SIGNALING" => Ok(ProposalKind::Signaling), + "PROPOSAL_KIND_EMERGENCY" => Ok(ProposalKind::Emergency), + "PROPOSAL_KIND_PARAMETER_CHANGE" => Ok(ProposalKind::ParameterChange), + "PROPOSAL_KIND_COMMUNITY_POOL_SPEND" => Ok(ProposalKind::CommunityPoolSpend), + "PROPOSAL_KIND_UPGRADE_PLAN" => Ok(ProposalKind::UpgradePlan), + "PROPOSAL_KIND_FREEZE_IBC_CLIENT" => Ok(ProposalKind::FreezeIbcClient), + "PROPOSAL_KIND_UNFREEZE_IBC_CLIENT" => Ok(ProposalKind::UnfreezeIbcClient), + _ => Err(serde::de::Error::unknown_variant(value, FIELDS)), + } + } + } + deserializer.deserialize_any(GeneratedVisitor) + } +} impl serde::Serialize for ProposalListRequest { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result diff --git a/crates/proto/src/gen/proto_descriptor.bin.no_lfs b/crates/proto/src/gen/proto_descriptor.bin.no_lfs index 9184afa862..3734b128d3 100644 Binary files a/crates/proto/src/gen/proto_descriptor.bin.no_lfs and b/crates/proto/src/gen/proto_descriptor.bin.no_lfs differ diff --git a/proto/penumbra/penumbra/core/component/governance/v1/governance.proto b/proto/penumbra/penumbra/core/component/governance/v1/governance.proto index 7f015c6668..25d7ae69fc 100644 --- a/proto/penumbra/penumbra/core/component/governance/v1/governance.proto +++ b/proto/penumbra/penumbra/core/component/governance/v1/governance.proto @@ -323,6 +323,19 @@ message Proposal { } } +// All the different kinds of proposals. +enum ProposalKind { + // To make the linter happy + PROPOSAL_KIND_UNSPECIFIED = 0; + PROPOSAL_KIND_SIGNALING = 1; + PROPOSAL_KIND_EMERGENCY = 2; + PROPOSAL_KIND_PARAMETER_CHANGE = 3; + PROPOSAL_KIND_COMMUNITY_POOL_SPEND = 4; + PROPOSAL_KIND_UPGRADE_PLAN = 5; + PROPOSAL_KIND_FREEZE_IBC_CLIENT = 6; + PROPOSAL_KIND_UNFREEZE_IBC_CLIENT = 7; +} + // Query operations for the governance component. service QueryService { rpc ProposalInfo(ProposalInfoRequest) returns (ProposalInfoResponse); @@ -532,6 +545,8 @@ message Ratio { message EventDelegatorVote { // The delegator vote. DelegatorVote vote = 1; + // The corresponding validator's identity key. + keys.v1.IdentityKey validator_identity_key = 2; } message EventProposalDepositClaim { @@ -542,6 +557,8 @@ message EventProposalDepositClaim { message EventValidatorVote { // The validator vote. ValidatorVote vote = 1; + // The validator's voting power at the time of the proposal's start. + uint64 voting_power = 2; } message EventProposalWithdraw { @@ -552,10 +569,14 @@ message EventProposalWithdraw { message EventProposalSubmit { // Details on the submitted proposal. ProposalSubmit submit = 1; + // The start height for the proposal. + uint64 start_height = 2; + // The end height for the proposal. + uint64 end_height = 3; } -message EventEnactProposal { - // The enacted proposal. +message EventProposalPassed { + // The passed proposal. Proposal proposal = 1; }