diff --git a/crates/core/app/src/params/change.rs b/crates/core/app/src/params/change.rs index 9f96c61478..b5b69fc5ff 100644 --- a/crates/core/app/src/params/change.rs +++ b/crates/core/app/src/params/change.rs @@ -55,10 +55,7 @@ impl AppParameters { outbound_ics20_transfers_enabled: _, }, sct_params: SctParameters { epoch_duration }, - shielded_pool_params: - ShieldedPoolParameters { - fixed_fmd_params: _, - }, + shielded_pool_params: ShieldedPoolParameters { fmd_meta_params: _ }, stake_params: StakeParameters { active_validator_limit, @@ -149,10 +146,7 @@ impl AppParameters { outbound_ics20_transfers_enabled, }, sct_params: SctParameters { epoch_duration }, - shielded_pool_params: - ShieldedPoolParameters { - fixed_fmd_params: _, - }, + shielded_pool_params: ShieldedPoolParameters { fmd_meta_params: _ }, stake_params: StakeParameters { active_validator_limit, diff --git a/crates/core/component/shielded-pool/src/fmd.rs b/crates/core/component/shielded-pool/src/fmd.rs index c363520ae4..0d0ac6671f 100644 --- a/crates/core/component/shielded-pool/src/fmd.rs +++ b/crates/core/component/shielded-pool/src/fmd.rs @@ -1,3 +1,4 @@ +use anyhow::anyhow; use decaf377_fmd::Precision; use penumbra_proto::{core::component::shielded_pool::v1 as pb, DomainType}; use serde::{Deserialize, Serialize}; @@ -45,3 +46,45 @@ impl Default for Parameters { } } } + +/// Meta parameters are an algorithm for dynamically choosing FMD parameters. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(try_from = "pb::FmdMetaParameters", into = "pb::FmdMetaParameters")] +pub enum MetaParameters { + /// Used a fixed precision forever. + Fixed(Precision), +} + +impl TryFrom for MetaParameters { + type Error = anyhow::Error; + + fn try_from(value: pb::FmdMetaParameters) -> Result { + match value.algorithm.ok_or(anyhow!("missing algorithm"))? { + pb::fmd_meta_parameters::Algorithm::FixedPrecisionBits(p) => { + Ok(MetaParameters::Fixed(Precision::new(p as u8)?)) + } + } + } +} + +impl From for pb::FmdMetaParameters { + fn from(value: MetaParameters) -> Self { + match value { + MetaParameters::Fixed(p) => pb::FmdMetaParameters { + algorithm: Some(pb::fmd_meta_parameters::Algorithm::FixedPrecisionBits( + p.bits().into(), + )), + }, + } + } +} + +impl DomainType for MetaParameters { + type Proto = pb::FmdMetaParameters; +} + +impl Default for MetaParameters { + fn default() -> Self { + Self::Fixed(Precision::default()) + } +} diff --git a/crates/core/component/shielded-pool/src/params.rs b/crates/core/component/shielded-pool/src/params.rs index 8c59d0c40c..6658420fcc 100644 --- a/crates/core/component/shielded-pool/src/params.rs +++ b/crates/core/component/shielded-pool/src/params.rs @@ -11,7 +11,7 @@ use crate::fmd; into = "pb::ShieldedPoolParameters" )] pub struct ShieldedPoolParameters { - pub fixed_fmd_params: fmd::Parameters, + pub fmd_meta_params: fmd::MetaParameters, } impl DomainType for ShieldedPoolParameters { @@ -23,9 +23,9 @@ impl TryFrom for ShieldedPoolParameters { fn try_from(msg: pb::ShieldedPoolParameters) -> anyhow::Result { Ok(ShieldedPoolParameters { - fixed_fmd_params: msg - .fixed_fmd_params - .ok_or_else(|| anyhow::anyhow!("missing fmd_parameters"))? + fmd_meta_params: msg + .fmd_meta_params + .ok_or_else(|| anyhow::anyhow!("missing fmd_meta_params"))? .try_into()?, }) } @@ -33,8 +33,10 @@ impl TryFrom for ShieldedPoolParameters { impl From for pb::ShieldedPoolParameters { fn from(params: ShieldedPoolParameters) -> Self { + #[allow(deprecated)] pb::ShieldedPoolParameters { - fixed_fmd_params: Some(params.fixed_fmd_params.into()), + fmd_meta_params: Some(params.fmd_meta_params.into()), + fixed_fmd_params: None, } } } diff --git a/crates/proto/src/gen/penumbra.core.component.shielded_pool.v1.rs b/crates/proto/src/gen/penumbra.core.component.shielded_pool.v1.rs index 3bcf1abb61..712b705a48 100644 --- a/crates/proto/src/gen/penumbra.core.component.shielded_pool.v1.rs +++ b/crates/proto/src/gen/penumbra.core.component.shielded_pool.v1.rs @@ -2,8 +2,11 @@ #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ShieldedPoolParameters { + #[deprecated] #[prost(message, optional, tag = "1")] pub fixed_fmd_params: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub fmd_meta_params: ::core::option::Option, } impl ::prost::Name for ShieldedPoolParameters { const NAME: &'static str = "ShieldedPoolParameters"; @@ -57,6 +60,30 @@ impl ::prost::Name for GenesisContent { ) } } +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FmdMetaParameters { + #[prost(oneof = "fmd_meta_parameters::Algorithm", tags = "1")] + pub algorithm: ::core::option::Option, +} +/// Nested message and enum types in `FmdMetaParameters`. +pub mod fmd_meta_parameters { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Algorithm { + #[prost(uint32, tag = "1")] + FixedPrecisionBits(u32), + } +} +impl ::prost::Name for FmdMetaParameters { + const NAME: &'static str = "FmdMetaParameters"; + const PACKAGE: &'static str = "penumbra.core.component.shielded_pool.v1"; + fn full_name() -> ::prost::alloc::string::String { + ::prost::alloc::format!( + "penumbra.core.component.shielded_pool.v1.{}", Self::NAME + ) + } +} /// Parameters for Fuzzy Message Detection #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] diff --git a/crates/proto/src/gen/penumbra.core.component.shielded_pool.v1.serde.rs b/crates/proto/src/gen/penumbra.core.component.shielded_pool.v1.serde.rs index 04f1e6056c..a1e86d33d3 100644 --- a/crates/proto/src/gen/penumbra.core.component.shielded_pool.v1.serde.rs +++ b/crates/proto/src/gen/penumbra.core.component.shielded_pool.v1.serde.rs @@ -381,6 +381,106 @@ impl<'de> serde::Deserialize<'de> for EventSpend { deserializer.deserialize_struct("penumbra.core.component.shielded_pool.v1.EventSpend", FIELDS, GeneratedVisitor) } } +impl serde::Serialize for FmdMetaParameters { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if self.algorithm.is_some() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("penumbra.core.component.shielded_pool.v1.FmdMetaParameters", len)?; + if let Some(v) = self.algorithm.as_ref() { + match v { + fmd_meta_parameters::Algorithm::FixedPrecisionBits(v) => { + struct_ser.serialize_field("fixedPrecisionBits", v)?; + } + } + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for FmdMetaParameters { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "fixed_precision_bits", + "fixedPrecisionBits", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + FixedPrecisionBits, + __SkipField__, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "fixedPrecisionBits" | "fixed_precision_bits" => Ok(GeneratedField::FixedPrecisionBits), + _ => Ok(GeneratedField::__SkipField__), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = FmdMetaParameters; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct penumbra.core.component.shielded_pool.v1.FmdMetaParameters") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut algorithm__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::FixedPrecisionBits => { + if algorithm__.is_some() { + return Err(serde::de::Error::duplicate_field("fixedPrecisionBits")); + } + algorithm__ = map_.next_value::<::std::option::Option<::pbjson::private::NumberDeserialize<_>>>()?.map(|x| fmd_meta_parameters::Algorithm::FixedPrecisionBits(x.0)); + } + GeneratedField::__SkipField__ => { + let _ = map_.next_value::()?; + } + } + } + Ok(FmdMetaParameters { + algorithm: algorithm__, + }) + } + } + deserializer.deserialize_struct("penumbra.core.component.shielded_pool.v1.FmdMetaParameters", FIELDS, GeneratedVisitor) + } +} impl serde::Serialize for FmdParameters { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result @@ -2052,10 +2152,16 @@ impl serde::Serialize for ShieldedPoolParameters { if self.fixed_fmd_params.is_some() { len += 1; } + if self.fmd_meta_params.is_some() { + len += 1; + } let mut struct_ser = serializer.serialize_struct("penumbra.core.component.shielded_pool.v1.ShieldedPoolParameters", len)?; if let Some(v) = self.fixed_fmd_params.as_ref() { struct_ser.serialize_field("fixedFmdParams", v)?; } + if let Some(v) = self.fmd_meta_params.as_ref() { + struct_ser.serialize_field("fmdMetaParams", v)?; + } struct_ser.end() } } @@ -2068,11 +2174,14 @@ impl<'de> serde::Deserialize<'de> for ShieldedPoolParameters { const FIELDS: &[&str] = &[ "fixed_fmd_params", "fixedFmdParams", + "fmd_meta_params", + "fmdMetaParams", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { FixedFmdParams, + FmdMetaParams, __SkipField__, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -2096,6 +2205,7 @@ impl<'de> serde::Deserialize<'de> for ShieldedPoolParameters { { match value { "fixedFmdParams" | "fixed_fmd_params" => Ok(GeneratedField::FixedFmdParams), + "fmdMetaParams" | "fmd_meta_params" => Ok(GeneratedField::FmdMetaParams), _ => Ok(GeneratedField::__SkipField__), } } @@ -2116,6 +2226,7 @@ impl<'de> serde::Deserialize<'de> for ShieldedPoolParameters { V: serde::de::MapAccess<'de>, { let mut fixed_fmd_params__ = None; + let mut fmd_meta_params__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::FixedFmdParams => { @@ -2124,6 +2235,12 @@ impl<'de> serde::Deserialize<'de> for ShieldedPoolParameters { } fixed_fmd_params__ = map_.next_value()?; } + GeneratedField::FmdMetaParams => { + if fmd_meta_params__.is_some() { + return Err(serde::de::Error::duplicate_field("fmdMetaParams")); + } + fmd_meta_params__ = map_.next_value()?; + } GeneratedField::__SkipField__ => { let _ = map_.next_value::()?; } @@ -2131,6 +2248,7 @@ impl<'de> serde::Deserialize<'de> for ShieldedPoolParameters { } Ok(ShieldedPoolParameters { fixed_fmd_params: fixed_fmd_params__, + fmd_meta_params: fmd_meta_params__, }) } } diff --git a/crates/proto/src/gen/proto_descriptor.bin.no_lfs b/crates/proto/src/gen/proto_descriptor.bin.no_lfs index d2be9305bf..6876962a06 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/shielded_pool/v1/shielded_pool.proto b/proto/penumbra/penumbra/core/component/shielded_pool/v1/shielded_pool.proto index 46555ca999..54e0f6f3f6 100644 --- a/proto/penumbra/penumbra/core/component/shielded_pool/v1/shielded_pool.proto +++ b/proto/penumbra/penumbra/core/component/shielded_pool/v1/shielded_pool.proto @@ -10,7 +10,8 @@ import "penumbra/crypto/tct/v1/tct.proto"; // Configuration data for the shielded pool component. message ShieldedPoolParameters { - FmdParameters fixed_fmd_params = 1; + FmdParameters fixed_fmd_params = 1 [deprecated = true]; + FmdMetaParameters fmd_meta_params = 2; } // Genesis data for the shielded pool component. @@ -26,6 +27,12 @@ message GenesisContent { repeated Allocation allocations = 3; } +message FmdMetaParameters { + oneof algorithm { + uint32 fixed_precision_bits = 1; + } +} + // Parameters for Fuzzy Message Detection message FmdParameters { uint32 precision_bits = 1;