Skip to content

Commit

Permalink
Add extension traits for managing clues
Browse files Browse the repository at this point in the history
  • Loading branch information
cronokirby committed Apr 12, 2024
1 parent 2ff11e9 commit 447b75e
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::sync::Arc;

use crate::fmd::should_update_fmd_params;
use crate::fmd::{should_update_fmd_params, ClueManagerInternal as _};
use crate::params::ShieldedPoolParameters;
use crate::{fmd, genesis, state_key};
use anyhow::anyhow;
Expand Down Expand Up @@ -83,7 +83,11 @@ impl Component for ShieldedPool {
.get_current_fmd_parameters()
.await
.expect("should be able to read state");
let new = meta_params.updated_fmd_params(&old, height);
let clue_count_delta = state
.flush_clue_count()
.await
.expect("should be able to read state");
let new = meta_params.updated_fmd_params(&old, height, clue_count_delta);
state.put_previous_fmd_parameters(old);
state.put_current_fmd_parameters(new);
}
Expand Down
96 changes: 90 additions & 6 deletions crates/core/component/shielded-pool/src/fmd.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use anyhow::anyhow;
use decaf377_fmd::Precision;
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use cnidarium::{StateRead, StateWrite};
use decaf377_fmd::{Clue, Precision};
use penumbra_proto::{
core::component::shielded_pool::v1::{self as pb},
DomainType,
DomainType, StateWriteProto,
};
use penumbra_txhash::TransactionId;
use serde::{Deserialize, Serialize};

pub mod state_key;
Expand Down Expand Up @@ -35,7 +38,7 @@ impl DomainType for Parameters {
impl TryFrom<pb::FmdParameters> for Parameters {
type Error = anyhow::Error;

fn try_from(msg: pb::FmdParameters) -> Result<Self, Self::Error> {
fn try_from(msg: pb::FmdParameters) -> Result<Self> {
Ok(Parameters {
precision: msg.precision_bits.try_into()?,
as_of_block_height: msg.as_of_block_height,
Expand Down Expand Up @@ -72,7 +75,7 @@ pub enum MetaParameters {
impl TryFrom<pb::FmdMetaParameters> for MetaParameters {
type Error = anyhow::Error;

fn try_from(value: pb::FmdMetaParameters) -> Result<Self, Self::Error> {
fn try_from(value: pb::FmdMetaParameters) -> Result<Self> {
match value.algorithm.ok_or(anyhow!("missing algorithm"))? {
pb::fmd_meta_parameters::Algorithm::FixedPrecisionBits(p) => {
Ok(MetaParameters::Fixed(Precision::new(p as u8)?))
Expand Down Expand Up @@ -104,7 +107,7 @@ impl Default for MetaParameters {
}

impl MetaParameters {
pub fn updated_fmd_params(&self, _old: &Parameters, height: u64) -> Parameters {
pub fn updated_fmd_params(&self, _old: &Parameters, height: u64, _clue_count_delta: (u64, u64)) -> Parameters {
match *self {
MetaParameters::Fixed(precision) => Parameters {
precision,
Expand All @@ -113,3 +116,84 @@ impl MetaParameters {
}
}
}

#[async_trait]
trait ClueWriteExt: StateWrite {
fn put_current_clue_count(&mut self, count: u64) {
self.put_raw(
state_key::clue_count::current().to_string(),
count.to_be_bytes().to_vec(),
)
}

fn put_previous_clue_count(&mut self, count: u64) {
self.put_raw(
state_key::clue_count::previous().to_string(),
count.to_be_bytes().to_vec(),
)
}
}

impl<T: StateWrite + ?Sized> ClueWriteExt for T {}

#[async_trait]
trait ClueReadExt: StateRead {
async fn get_current_clue_count(&self) -> Result<u64> {
Ok(u64::from_be_bytes(
self.get_raw(state_key::clue_count::current())
.await?
.ok_or(anyhow!("no current clue count"))?
.as_slice()
.try_into()?,
))
}

async fn get_previous_clue_count(&self) -> Result<u64> {
Ok(u64::from_be_bytes(
self.get_raw(state_key::clue_count::previous())
.await?
.ok_or(anyhow!("no current clue count"))?
.as_slice()
.try_into()?,
))
}
}

impl<T: StateRead + ?Sized> ClueReadExt for T {}

#[async_trait]
pub trait ClueManager: StateRead + StateWrite {
async fn record_clue(&mut self, clue: Clue, tx: TransactionId) -> Result<()> {
// Update count
{
let count = self.get_current_clue_count().await?;
self.put_current_clue_count(count.saturating_add(1));
}
self.record_proto(pb::EventClue {
clue: Some(clue.into()),
tx: Some(tx.into())
});
Ok(())
}
}

impl<T: StateRead + StateWrite> ClueManager for T {}

#[async_trait]
pub(crate) trait ClueManagerInternal: ClueManager {
fn init(&mut self) {
self.put_current_clue_count(0);
self.put_previous_clue_count(0);
}

/// Flush the clue counts, returning the previous and current counts
async fn flush_clue_count(&mut self) -> Result<(u64, u64)> {
let previous = self.get_previous_clue_count().await?;
let current = self.get_current_clue_count().await?;
self.put_previous_clue_count(current);
self.put_current_clue_count(0);
Ok((previous, current))
}
}

impl<T: ClueManager> ClueManagerInternal for T {}
10 changes: 10 additions & 0 deletions crates/core/component/shielded-pool/src/fmd/state_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,13 @@ pub mod parameters {
"shielded_pool/fmd_parameters/previous"
}
}

pub(super) mod clue_count {
pub fn current() -> &'static str {
"shielded_pool/fmd_clue_count/current"
}

pub fn previous() -> &'static str {
"shielded_pool/fmd_clue_count/previous"
}
}
1 change: 1 addition & 0 deletions crates/core/component/shielded-pool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub mod output;
pub mod spend;

pub use convert::{ConvertCircuit, ConvertProof, ConvertProofPrivate, ConvertProofPublic};
pub use fmd::ClueManager;
pub use nullifier_derivation::{
NullifierDerivationCircuit, NullifierDerivationProof, NullifierDerivationProofPrivate,
NullifierDerivationProofPublic,
Expand Down
20 changes: 20 additions & 0 deletions crates/proto/src/gen/penumbra.core.component.shielded_pool.v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,26 @@ impl ::prost::Name for EventOutput {
)
}
}
/// ABCI Event recording a clue.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EventClue {
#[prost(message, optional, tag = "1")]
pub clue: ::core::option::Option<
super::super::super::super::crypto::decaf377_fmd::v1::Clue,
>,
#[prost(message, optional, tag = "2")]
pub tx: ::core::option::Option<super::super::super::txhash::v1::TransactionId>,
}
impl ::prost::Name for EventClue {
const NAME: &'static str = "EventClue";
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
)
}
}
/// The body of a spend description, containing only the effecting data
/// describing changes to the ledger, and not the authorizing data that allows
/// those changes to be performed.
Expand Down
112 changes: 112 additions & 0 deletions crates/proto/src/gen/penumbra.core.component.shielded_pool.v1.serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,118 @@ impl<'de> serde::Deserialize<'de> for AssetMetadataByIdResponse {
deserializer.deserialize_struct("penumbra.core.component.shielded_pool.v1.AssetMetadataByIdResponse", FIELDS, GeneratedVisitor)
}
}
impl serde::Serialize for EventClue {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut len = 0;
if self.clue.is_some() {
len += 1;
}
if self.tx.is_some() {
len += 1;
}
let mut struct_ser = serializer.serialize_struct("penumbra.core.component.shielded_pool.v1.EventClue", len)?;
if let Some(v) = self.clue.as_ref() {
struct_ser.serialize_field("clue", v)?;
}
if let Some(v) = self.tx.as_ref() {
struct_ser.serialize_field("tx", v)?;
}
struct_ser.end()
}
}
impl<'de> serde::Deserialize<'de> for EventClue {
#[allow(deprecated)]
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
const FIELDS: &[&str] = &[
"clue",
"tx",
];

#[allow(clippy::enum_variant_names)]
enum GeneratedField {
Clue,
Tx,
__SkipField__,
}
impl<'de> serde::Deserialize<'de> for GeneratedField {
fn deserialize<D>(deserializer: D) -> std::result::Result<GeneratedField, D::Error>
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<E>(self, value: &str) -> std::result::Result<GeneratedField, E>
where
E: serde::de::Error,
{
match value {
"clue" => Ok(GeneratedField::Clue),
"tx" => Ok(GeneratedField::Tx),
_ => Ok(GeneratedField::__SkipField__),
}
}
}
deserializer.deserialize_identifier(GeneratedVisitor)
}
}
struct GeneratedVisitor;
impl<'de> serde::de::Visitor<'de> for GeneratedVisitor {
type Value = EventClue;

fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("struct penumbra.core.component.shielded_pool.v1.EventClue")
}

fn visit_map<V>(self, mut map_: V) -> std::result::Result<EventClue, V::Error>
where
V: serde::de::MapAccess<'de>,
{
let mut clue__ = None;
let mut tx__ = None;
while let Some(k) = map_.next_key()? {
match k {
GeneratedField::Clue => {
if clue__.is_some() {
return Err(serde::de::Error::duplicate_field("clue"));
}
clue__ = map_.next_value()?;
}
GeneratedField::Tx => {
if tx__.is_some() {
return Err(serde::de::Error::duplicate_field("tx"));
}
tx__ = map_.next_value()?;
}
GeneratedField::__SkipField__ => {
let _ = map_.next_value::<serde::de::IgnoredAny>()?;
}
}
}
Ok(EventClue {
clue: clue__,
tx: tx__,
})
}
}
deserializer.deserialize_struct("penumbra.core.component.shielded_pool.v1.EventClue", FIELDS, GeneratedVisitor)
}
}
impl serde::Serialize for EventOutput {
#[allow(deprecated)]
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
Expand Down
Binary file modified crates/proto/src/gen/proto_descriptor.bin.no_lfs
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import "penumbra/core/asset/v1/asset.proto";
import "penumbra/core/component/sct/v1/sct.proto";
import "penumbra/core/keys/v1/keys.proto";
import "penumbra/core/num/v1/num.proto";
import "penumbra/core/txhash/v1/txhash.proto";
import "penumbra/crypto/decaf377_fmd/v1/decaf377_fmd.proto";
import "penumbra/crypto/decaf377_rdsa/v1/decaf377_rdsa.proto";
import "penumbra/crypto/tct/v1/tct.proto";

Expand Down Expand Up @@ -104,6 +106,12 @@ message EventOutput {
crypto.tct.v1.StateCommitment note_commitment = 1;
}

// ABCI Event recording a clue.
message EventClue {
crypto.decaf377_fmd.v1.Clue clue = 1;
txhash.v1.TransactionId tx = 2;
}

// The body of a spend description, containing only the effecting data
// describing changes to the ledger, and not the authorizing data that allows
// those changes to be performed.
Expand Down

0 comments on commit 447b75e

Please sign in to comment.