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