penumbra_sdk_shielded_pool/component/
fmd.rs

1use anyhow::Result;
2use async_trait::async_trait;
3use cnidarium::{StateRead, StateWrite};
4use decaf377_fmd::Clue;
5use penumbra_sdk_proto::{
6    core::component::shielded_pool::v1::{self as pb},
7    StateWriteProto,
8};
9use penumbra_sdk_txhash::TransactionId;
10
11use crate::fmd::state_key;
12
13#[async_trait]
14trait ClueWriteExt: StateWrite {
15    fn put_current_clue_count(&mut self, count: u64) {
16        self.put_raw(
17            state_key::clue_count::current().to_string(),
18            count.to_be_bytes().to_vec(),
19        )
20    }
21
22    fn put_previous_clue_count(&mut self, count: u64) {
23        self.put_raw(
24            state_key::clue_count::previous().to_string(),
25            count.to_be_bytes().to_vec(),
26        )
27    }
28}
29
30impl<T: StateWrite + ?Sized> ClueWriteExt for T {}
31
32#[async_trait]
33trait ClueReadExt: StateRead {
34    // The implementation for both of these methods will return 0 on a missing key,
35    // this is because the clue count is just used to tally clues over time,
36    // and so 0 will always be a good starting value.
37    async fn get_current_clue_count(&self) -> Result<u64> {
38        Ok(self
39            .get_raw(state_key::clue_count::current())
40            .await?
41            .map(|x| x.as_slice().try_into())
42            .transpose()?
43            .map(u64::from_be_bytes)
44            .unwrap_or(0u64))
45    }
46
47    async fn get_previous_clue_count(&self) -> Result<u64> {
48        Ok(self
49            .get_raw(state_key::clue_count::previous())
50            .await?
51            .map(|x| x.as_slice().try_into())
52            .transpose()?
53            .map(u64::from_be_bytes)
54            .unwrap_or(0u64))
55    }
56}
57
58impl<T: StateRead + ?Sized> ClueReadExt for T {}
59
60#[async_trait]
61pub trait ClueManager: StateRead + StateWrite {
62    async fn record_clue(&mut self, clue: Clue, tx: TransactionId) -> Result<()> {
63        {
64            let count = self.get_current_clue_count().await?;
65            self.put_current_clue_count(count.saturating_add(1));
66        }
67        self.record_proto(pb::EventBroadcastClue {
68            clue: Some(clue.into()),
69            tx: Some(tx.into()),
70        });
71        Ok(())
72    }
73}
74
75impl<T: StateRead + StateWrite> ClueManager for T {}
76
77#[async_trait]
78pub(crate) trait ClueManagerInternal: ClueManager {
79    /// Flush the clue counts, returning the previous and current counts
80    async fn flush_clue_count(&mut self) -> Result<(u64, u64)> {
81        let previous = self.get_previous_clue_count().await?;
82        let current = self.get_current_clue_count().await?;
83        self.put_previous_clue_count(current);
84        self.put_current_clue_count(0);
85        Ok((previous, current))
86    }
87}
88
89impl<T: ClueManager> ClueManagerInternal for T {}