1use core::fmt;
2
3#[cfg(feature = "std")]
4use std::error::Error;
5
6use zcash_protocol::consensus::NetworkType;
7
8use crate::{kind::*, AddressKind, ZcashAddress};
9
10#[derive(Debug)]
12pub struct UnsupportedAddress(&'static str);
13
14impl fmt::Display for UnsupportedAddress {
15    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16        write!(f, "Zcash {} addresses are not supported", self.0)
17    }
18}
19
20#[derive(Debug)]
22pub enum ConversionError<E> {
23    IncorrectNetwork {
25        expected: NetworkType,
26        actual: NetworkType,
27    },
28    Unsupported(UnsupportedAddress),
30    User(E),
32}
33
34impl<E> From<E> for ConversionError<E> {
35    fn from(e: E) -> Self {
36        ConversionError::User(e)
37    }
38}
39
40impl<E: fmt::Display> fmt::Display for ConversionError<E> {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        match self {
43            Self::IncorrectNetwork { expected, actual } => write!(
44                f,
45                "Address is for {:?} but we expected {:?}",
46                actual, expected,
47            ),
48            Self::Unsupported(e) => e.fmt(f),
49            Self::User(e) => e.fmt(f),
50        }
51    }
52}
53
54#[cfg(feature = "std")]
55impl Error for UnsupportedAddress {}
56#[cfg(feature = "std")]
57impl<E: Error + 'static> Error for ConversionError<E> {
58    fn source(&self) -> Option<&(dyn Error + 'static)> {
59        match self {
60            ConversionError::IncorrectNetwork { .. } | ConversionError::Unsupported(_) => None,
61            ConversionError::User(e) => Some(e),
62        }
63    }
64}
65
66pub trait TryFromAddress: Sized {
109    type Error;
112
113    fn try_from_sprout(
114        net: NetworkType,
115        data: [u8; 64],
116    ) -> Result<Self, ConversionError<Self::Error>> {
117        let _ = (net, data);
118        Err(ConversionError::Unsupported(UnsupportedAddress("Sprout")))
119    }
120
121    fn try_from_sapling(
122        net: NetworkType,
123        data: [u8; 43],
124    ) -> Result<Self, ConversionError<Self::Error>> {
125        let _ = (net, data);
126        Err(ConversionError::Unsupported(UnsupportedAddress("Sapling")))
127    }
128
129    fn try_from_unified(
130        net: NetworkType,
131        data: unified::Address,
132    ) -> Result<Self, ConversionError<Self::Error>> {
133        let _ = (net, data);
134        Err(ConversionError::Unsupported(UnsupportedAddress("Unified")))
135    }
136
137    fn try_from_transparent_p2pkh(
138        net: NetworkType,
139        data: [u8; 20],
140    ) -> Result<Self, ConversionError<Self::Error>> {
141        let _ = (net, data);
142        Err(ConversionError::Unsupported(UnsupportedAddress(
143            "transparent P2PKH",
144        )))
145    }
146
147    fn try_from_transparent_p2sh(
148        net: NetworkType,
149        data: [u8; 20],
150    ) -> Result<Self, ConversionError<Self::Error>> {
151        let _ = (net, data);
152        Err(ConversionError::Unsupported(UnsupportedAddress(
153            "transparent P2SH",
154        )))
155    }
156
157    fn try_from_tex(
158        net: NetworkType,
159        data: [u8; 20],
160    ) -> Result<Self, ConversionError<Self::Error>> {
161        let _ = (net, data);
162        Err(ConversionError::Unsupported(UnsupportedAddress(
163            "transparent-source restricted P2PKH",
164        )))
165    }
166}
167
168impl<T: TryFromAddress> TryFromAddress for (NetworkType, T) {
169    type Error = T::Error;
170
171    fn try_from_sprout(
172        net: NetworkType,
173        data: [u8; 64],
174    ) -> Result<Self, ConversionError<Self::Error>> {
175        T::try_from_sprout(net, data).map(|addr| (net, addr))
176    }
177
178    fn try_from_sapling(
179        net: NetworkType,
180        data: [u8; 43],
181    ) -> Result<Self, ConversionError<Self::Error>> {
182        T::try_from_sapling(net, data).map(|addr| (net, addr))
183    }
184
185    fn try_from_unified(
186        net: NetworkType,
187        data: unified::Address,
188    ) -> Result<Self, ConversionError<Self::Error>> {
189        T::try_from_unified(net, data).map(|addr| (net, addr))
190    }
191
192    fn try_from_transparent_p2pkh(
193        net: NetworkType,
194        data: [u8; 20],
195    ) -> Result<Self, ConversionError<Self::Error>> {
196        T::try_from_transparent_p2pkh(net, data).map(|addr| (net, addr))
197    }
198
199    fn try_from_transparent_p2sh(
200        net: NetworkType,
201        data: [u8; 20],
202    ) -> Result<Self, ConversionError<Self::Error>> {
203        T::try_from_transparent_p2sh(net, data).map(|addr| (net, addr))
204    }
205
206    fn try_from_tex(
207        net: NetworkType,
208        data: [u8; 20],
209    ) -> Result<Self, ConversionError<Self::Error>> {
210        T::try_from_tex(net, data).map(|addr| (net, addr))
211    }
212}
213
214pub trait Converter<T> {
247    type Error;
250
251    fn convert_sprout(
252        &self,
253        net: NetworkType,
254        data: [u8; 64],
255    ) -> Result<T, ConversionError<Self::Error>> {
256        let _ = (net, data);
257        Err(ConversionError::Unsupported(UnsupportedAddress("Sprout")))
258    }
259
260    fn convert_sapling(
261        &self,
262        net: NetworkType,
263        data: [u8; 43],
264    ) -> Result<T, ConversionError<Self::Error>> {
265        let _ = (net, data);
266        Err(ConversionError::Unsupported(UnsupportedAddress("Sapling")))
267    }
268
269    fn convert_unified(
270        &self,
271        net: NetworkType,
272        data: unified::Address,
273    ) -> Result<T, ConversionError<Self::Error>> {
274        let _ = (net, data);
275        Err(ConversionError::Unsupported(UnsupportedAddress("Unified")))
276    }
277
278    fn convert_transparent_p2pkh(
279        &self,
280        net: NetworkType,
281        data: [u8; 20],
282    ) -> Result<T, ConversionError<Self::Error>> {
283        let _ = (net, data);
284        Err(ConversionError::Unsupported(UnsupportedAddress(
285            "transparent P2PKH",
286        )))
287    }
288
289    fn convert_transparent_p2sh(
290        &self,
291        net: NetworkType,
292        data: [u8; 20],
293    ) -> Result<T, ConversionError<Self::Error>> {
294        let _ = (net, data);
295        Err(ConversionError::Unsupported(UnsupportedAddress(
296            "transparent P2SH",
297        )))
298    }
299
300    fn convert_tex(
301        &self,
302        net: NetworkType,
303        data: [u8; 20],
304    ) -> Result<T, ConversionError<Self::Error>> {
305        let _ = (net, data);
306        Err(ConversionError::Unsupported(UnsupportedAddress(
307            "transparent-source restricted P2PKH",
308        )))
309    }
310}
311
312pub trait ToAddress: private::Sealed {
345    fn from_sprout(net: NetworkType, data: [u8; 64]) -> Self;
346
347    fn from_sapling(net: NetworkType, data: [u8; 43]) -> Self;
348
349    fn from_unified(net: NetworkType, data: unified::Address) -> Self;
350
351    fn from_transparent_p2pkh(net: NetworkType, data: [u8; 20]) -> Self;
352
353    fn from_transparent_p2sh(net: NetworkType, data: [u8; 20]) -> Self;
354
355    fn from_tex(net: NetworkType, data: [u8; 20]) -> Self;
356}
357
358impl ToAddress for ZcashAddress {
359    fn from_sprout(net: NetworkType, data: [u8; 64]) -> Self {
360        ZcashAddress {
361            net: if let NetworkType::Regtest = net {
362                NetworkType::Test
363            } else {
364                net
365            },
366            kind: AddressKind::Sprout(data),
367        }
368    }
369
370    fn from_sapling(net: NetworkType, data: [u8; 43]) -> Self {
371        ZcashAddress {
372            net,
373            kind: AddressKind::Sapling(data),
374        }
375    }
376
377    fn from_unified(net: NetworkType, data: unified::Address) -> Self {
378        ZcashAddress {
379            net,
380            kind: AddressKind::Unified(data),
381        }
382    }
383
384    fn from_transparent_p2pkh(net: NetworkType, data: [u8; 20]) -> Self {
385        ZcashAddress {
386            net: if let NetworkType::Regtest = net {
387                NetworkType::Test
388            } else {
389                net
390            },
391            kind: AddressKind::P2pkh(data),
392        }
393    }
394
395    fn from_transparent_p2sh(net: NetworkType, data: [u8; 20]) -> Self {
396        ZcashAddress {
397            net: if let NetworkType::Regtest = net {
398                NetworkType::Test
399            } else {
400                net
401            },
402            kind: AddressKind::P2sh(data),
403        }
404    }
405
406    fn from_tex(net: NetworkType, data: [u8; 20]) -> Self {
407        ZcashAddress {
408            net,
409            kind: AddressKind::Tex(data),
410        }
411    }
412}
413
414mod private {
415    use crate::ZcashAddress;
416
417    pub trait Sealed {}
418    impl Sealed for ZcashAddress {}
419}