sapling_crypto/
address.rs

1use super::{
2    keys::{DiversifiedTransmissionKey, Diversifier},
3    note::{Note, Rseed},
4    value::NoteValue,
5};
6
7/// A Sapling payment address.
8///
9/// # Invariants
10///
11/// - `diversifier` is guaranteed to be valid for Sapling (only 50% of diversifiers are).
12/// - `pk_d` is guaranteed to be prime-order (i.e. in the prime-order subgroup of Jubjub,
13///   and not the identity).
14#[derive(Clone, Copy, Debug)]
15pub struct PaymentAddress {
16    pk_d: DiversifiedTransmissionKey,
17    diversifier: Diversifier,
18}
19
20impl PartialEq for PaymentAddress {
21    fn eq(&self, other: &Self) -> bool {
22        self.pk_d == other.pk_d && self.diversifier == other.diversifier
23    }
24}
25
26impl Eq for PaymentAddress {}
27
28impl PaymentAddress {
29    /// Constructs a PaymentAddress from a diversifier and a Jubjub point.
30    ///
31    /// Returns None if `diversifier` is not valid for Sapling, or `pk_d` is the identity.
32    /// Note that we cannot verify in this constructor that `pk_d` is derived from
33    /// `diversifier`, so addresses for which these values have no known relationship
34    /// (and therefore no-one can receive funds at them) can still be constructed.
35    pub fn from_parts(diversifier: Diversifier, pk_d: DiversifiedTransmissionKey) -> Option<Self> {
36        // Check that the diversifier is valid
37        diversifier.g_d()?;
38
39        Self::from_parts_unchecked(diversifier, pk_d)
40    }
41
42    /// Constructs a PaymentAddress from a diversifier and a Jubjub point.
43    ///
44    /// Returns None if `pk_d` is the identity. The caller must check that `diversifier`
45    /// is valid for Sapling.
46    pub(crate) fn from_parts_unchecked(
47        diversifier: Diversifier,
48        pk_d: DiversifiedTransmissionKey,
49    ) -> Option<Self> {
50        if pk_d.is_identity() {
51            None
52        } else {
53            Some(PaymentAddress { pk_d, diversifier })
54        }
55    }
56
57    /// Parses a PaymentAddress from bytes.
58    pub fn from_bytes(bytes: &[u8; 43]) -> Option<Self> {
59        let diversifier = {
60            let mut tmp = [0; 11];
61            tmp.copy_from_slice(&bytes[0..11]);
62            Diversifier(tmp)
63        };
64
65        let pk_d = DiversifiedTransmissionKey::from_bytes(bytes[11..43].try_into().unwrap());
66        if pk_d.is_some().into() {
67            // The remaining invariants are checked here.
68            PaymentAddress::from_parts(diversifier, pk_d.unwrap())
69        } else {
70            None
71        }
72    }
73
74    /// Returns the byte encoding of this `PaymentAddress`.
75    pub fn to_bytes(&self) -> [u8; 43] {
76        let mut bytes = [0; 43];
77        bytes[0..11].copy_from_slice(&self.diversifier.0);
78        bytes[11..].copy_from_slice(&self.pk_d.to_bytes());
79        bytes
80    }
81
82    /// Returns the [`Diversifier`] for this `PaymentAddress`.
83    pub fn diversifier(&self) -> &Diversifier {
84        &self.diversifier
85    }
86
87    /// Returns `pk_d` for this `PaymentAddress`.
88    pub fn pk_d(&self) -> &DiversifiedTransmissionKey {
89        &self.pk_d
90    }
91
92    pub(crate) fn g_d(&self) -> jubjub::SubgroupPoint {
93        self.diversifier.g_d().expect("checked at construction")
94    }
95
96    pub fn create_note(&self, value: NoteValue, rseed: Rseed) -> Note {
97        Note::from_parts(*self, value, rseed)
98    }
99}
100
101#[cfg(any(test, feature = "test-dependencies"))]
102pub(super) mod testing {
103    use proptest::prelude::*;
104
105    use super::{
106        super::keys::{testing::arb_incoming_viewing_key, Diversifier, SaplingIvk},
107        PaymentAddress,
108    };
109
110    pub fn arb_payment_address() -> impl Strategy<Value = PaymentAddress> {
111        arb_incoming_viewing_key().prop_flat_map(|ivk: SaplingIvk| {
112            any::<[u8; 11]>().prop_filter_map(
113                "Sampled diversifier must generate a valid Sapling payment address.",
114                move |d| ivk.to_payment_address(Diversifier(d)),
115            )
116        })
117    }
118}