zebra_chain/primitives/
address.rs

1//! `zcash_address` conversion to `zebra_chain` address types.
2//!
3//! Usage: <https://docs.rs/zcash_address/0.2.0/zcash_address/trait.TryFromAddress.html#examples>
4
5use zcash_address::unified::{self, Container};
6
7use crate::{parameters::NetworkKind, transparent, BoxError};
8
9/// Zcash address variants
10pub enum Address {
11    /// Transparent address
12    Transparent(transparent::Address),
13
14    /// Sapling address
15    Sapling {
16        /// Address' network kind
17        network: NetworkKind,
18
19        /// Sapling address
20        address: sapling_crypto::PaymentAddress,
21    },
22
23    /// Unified address
24    Unified {
25        /// Address' network kind
26        network: NetworkKind,
27
28        /// Unified address
29        unified_address: zcash_address::unified::Address,
30
31        /// Orchard address
32        orchard: Option<orchard::Address>,
33
34        /// Sapling address
35        sapling: Option<sapling_crypto::PaymentAddress>,
36
37        /// Transparent address
38        transparent: Option<transparent::Address>,
39    },
40}
41
42impl zcash_address::TryFromAddress for Address {
43    // TODO: crate::serialization::SerializationError
44    type Error = BoxError;
45
46    fn try_from_transparent_p2pkh(
47        network: zcash_address::Network,
48        data: [u8; 20],
49    ) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
50        Ok(Self::Transparent(transparent::Address::from_pub_key_hash(
51            network.into(),
52            data,
53        )))
54    }
55
56    fn try_from_transparent_p2sh(
57        network: zcash_address::Network,
58        data: [u8; 20],
59    ) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
60        Ok(Self::Transparent(transparent::Address::from_script_hash(
61            network.into(),
62            data,
63        )))
64    }
65
66    fn try_from_sapling(
67        network: zcash_address::Network,
68        data: [u8; 43],
69    ) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
70        let network = network.into();
71        sapling_crypto::PaymentAddress::from_bytes(&data)
72            .map(|address| Self::Sapling { address, network })
73            .ok_or_else(|| BoxError::from("not a valid sapling address").into())
74    }
75
76    fn try_from_unified(
77        network: zcash_address::Network,
78        unified_address: zcash_address::unified::Address,
79    ) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
80        let network = network.into();
81        let mut orchard = None;
82        let mut sapling = None;
83        let mut transparent = None;
84
85        for receiver in unified_address.items().into_iter() {
86            match receiver {
87                unified::Receiver::Orchard(data) => {
88                    orchard = orchard::Address::from_raw_address_bytes(&data).into();
89                    // ZIP 316: Consumers MUST reject Unified Addresses/Viewing Keys in
90                    // which any constituent Item does not meet the validation
91                    // requirements of its encoding.
92                    if orchard.is_none() {
93                        return Err(BoxError::from(
94                            "Unified Address contains an invalid Orchard receiver.",
95                        )
96                        .into());
97                    }
98                }
99                unified::Receiver::Sapling(data) => {
100                    sapling = sapling_crypto::PaymentAddress::from_bytes(&data);
101                    // ZIP 316: Consumers MUST reject Unified Addresses/Viewing Keys in
102                    // which any constituent Item does not meet the validation
103                    // requirements of its encoding.
104                    if sapling.is_none() {
105                        return Err(BoxError::from(
106                            "Unified Address contains an invalid Sapling receiver",
107                        )
108                        .into());
109                    }
110                }
111                unified::Receiver::P2pkh(data) => {
112                    transparent = Some(transparent::Address::from_pub_key_hash(network, data));
113                }
114                unified::Receiver::P2sh(data) => {
115                    transparent = Some(transparent::Address::from_script_hash(network, data));
116                }
117                unified::Receiver::Unknown { .. } => {
118                    return Err(BoxError::from("Unsupported receiver in a Unified Address.").into());
119                }
120            }
121        }
122
123        Ok(Self::Unified {
124            network,
125            unified_address,
126            orchard,
127            sapling,
128            transparent,
129        })
130    }
131}
132
133impl Address {
134    /// Returns the network for the address.
135    pub fn network(&self) -> NetworkKind {
136        match &self {
137            Self::Transparent(address) => address.network_kind(),
138            Self::Sapling { network, .. } | Self::Unified { network, .. } => *network,
139        }
140    }
141
142    /// Returns true if the address is PayToScriptHash
143    /// Returns false if the address is PayToPublicKeyHash or shielded.
144    pub fn is_script_hash(&self) -> bool {
145        match &self {
146            Self::Transparent(address) => address.is_script_hash(),
147            Self::Sapling { .. } | Self::Unified { .. } => false,
148        }
149    }
150
151    /// Returns true if address is of the [`Address::Transparent`] variant.
152    /// Returns false if otherwise.
153    pub fn is_transparent(&self) -> bool {
154        matches!(self, Self::Transparent(_))
155    }
156
157    /// Returns the payment address for transparent or sapling addresses.
158    pub fn payment_address(&self) -> Option<String> {
159        use zcash_address::{ToAddress, ZcashAddress};
160
161        match &self {
162            Self::Transparent(address) => Some(address.to_string()),
163            Self::Sapling { address, network } => {
164                let data = address.to_bytes();
165                let address = ZcashAddress::from_sapling(network.into(), data);
166                Some(address.encode())
167            }
168            Self::Unified { .. } => None,
169        }
170    }
171}
172
173impl From<zcash_address::Network> for NetworkKind {
174    fn from(network: zcash_address::Network) -> Self {
175        match network {
176            zcash_address::Network::Main => NetworkKind::Mainnet,
177            zcash_address::Network::Test => NetworkKind::Testnet,
178            zcash_address::Network::Regtest => NetworkKind::Regtest,
179        }
180    }
181}
182
183impl From<NetworkKind> for zcash_address::Network {
184    fn from(network: NetworkKind) -> Self {
185        match network {
186            NetworkKind::Mainnet => zcash_address::Network::Main,
187            NetworkKind::Testnet => zcash_address::Network::Test,
188            NetworkKind::Regtest => zcash_address::Network::Regtest,
189        }
190    }
191}
192
193impl From<&NetworkKind> for zcash_address::Network {
194    fn from(network: &NetworkKind) -> Self {
195        (*network).into()
196    }
197}