penumbra_sdk_transaction/plan/
clue.rs

1use decaf377_fmd::{Clue, Precision};
2use penumbra_sdk_keys::Address;
3use penumbra_sdk_proto::{core::transaction::v1 as pb, DomainType};
4
5use rand::{CryptoRng, RngCore};
6
7#[derive(Clone, Debug)]
8pub struct CluePlan {
9    pub address: Address,
10    pub precision: Precision,
11    pub rseed: [u8; 32],
12}
13
14impl CluePlan {
15    /// Create a new [`CluePlan`] associated with a given (possibly dummy) `Address`.
16    pub fn new<R: CryptoRng + RngCore>(
17        rng: &mut R,
18        address: Address,
19        precision: Precision,
20    ) -> CluePlan {
21        let mut rseed = [0u8; 32];
22        rng.fill_bytes(&mut rseed);
23        CluePlan {
24            address,
25            rseed,
26            precision,
27        }
28    }
29
30    /// Create a [`Clue`] from the [`CluePlan`].
31    pub fn clue(&self) -> Clue {
32        let clue_key = self.address.clue_key();
33        let expanded_clue_key = clue_key.expand_infallible();
34        expanded_clue_key
35            .create_clue_deterministic(self.precision, self.rseed)
36            .expect("can construct clue key")
37    }
38}
39
40impl DomainType for CluePlan {
41    type Proto = pb::CluePlan;
42}
43
44impl From<CluePlan> for pb::CluePlan {
45    fn from(msg: CluePlan) -> Self {
46        Self {
47            address: Some(msg.address.into()),
48            rseed: msg.rseed.to_vec(),
49            precision_bits: msg.precision.bits() as u64,
50        }
51    }
52}
53
54impl TryFrom<pb::CluePlan> for CluePlan {
55    type Error = anyhow::Error;
56    fn try_from(msg: pb::CluePlan) -> Result<Self, Self::Error> {
57        Ok(Self {
58            address: msg
59                .address
60                .ok_or_else(|| anyhow::anyhow!("missing address"))?
61                .try_into()?,
62            rseed: msg.rseed.as_slice().try_into()?,
63            precision: msg.precision_bits.try_into()?,
64        })
65    }
66}