1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
use decaf377_fmd::Clue;
use penumbra_keys::Address;
use penumbra_proto::{core::transaction::v1 as pb, DomainType};

use rand::{CryptoRng, RngCore};

#[derive(Clone, Debug)]
pub struct CluePlan {
    pub address: Address,
    pub precision_bits: usize,
    pub rseed: [u8; 32],
}

impl CluePlan {
    /// Create a new [`CluePlan`] associated with a given (possibly dummy) `Address`.
    pub fn new<R: CryptoRng + RngCore>(
        rng: &mut R,
        address: Address,
        precision_bits: usize,
    ) -> CluePlan {
        let mut rseed = [0u8; 32];
        rng.fill_bytes(&mut rseed);
        CluePlan {
            address,
            rseed,
            precision_bits,
        }
    }

    /// Create a [`Clue`] from the [`CluePlan`].
    pub fn clue(&self) -> Clue {
        let clue_key = self.address.clue_key();
        let expanded_clue_key = clue_key.expand_infallible();
        expanded_clue_key
            .create_clue_deterministic(self.precision_bits, self.rseed)
            .expect("can construct clue key")
    }
}

impl DomainType for CluePlan {
    type Proto = pb::CluePlan;
}

impl From<CluePlan> for pb::CluePlan {
    fn from(msg: CluePlan) -> Self {
        Self {
            address: Some(msg.address.into()),
            rseed: msg.rseed.to_vec(),
            precision_bits: msg.precision_bits as u64,
        }
    }
}

impl TryFrom<pb::CluePlan> for CluePlan {
    type Error = anyhow::Error;
    fn try_from(msg: pb::CluePlan) -> Result<Self, Self::Error> {
        Ok(Self {
            address: msg
                .address
                .ok_or_else(|| anyhow::anyhow!("missing address"))?
                .try_into()?,
            rseed: msg.rseed.as_slice().try_into()?,
            precision_bits: msg.precision_bits.try_into()?,
        })
    }
}