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 TryFromRawAddress: Sized {
117 type Error;
120
121 fn try_from_raw_sprout(data: [u8; 64]) -> Result<Self, ConversionError<Self::Error>> {
122 let _ = data;
123 Err(ConversionError::Unsupported(UnsupportedAddress("Sprout")))
124 }
125
126 fn try_from_raw_sapling(data: [u8; 43]) -> Result<Self, ConversionError<Self::Error>> {
127 let _ = data;
128 Err(ConversionError::Unsupported(UnsupportedAddress("Sapling")))
129 }
130
131 fn try_from_raw_unified(data: unified::Address) -> Result<Self, ConversionError<Self::Error>> {
132 let _ = data;
133 Err(ConversionError::Unsupported(UnsupportedAddress("Unified")))
134 }
135
136 fn try_from_raw_transparent_p2pkh(
137 data: [u8; 20],
138 ) -> Result<Self, ConversionError<Self::Error>> {
139 let _ = data;
140 Err(ConversionError::Unsupported(UnsupportedAddress(
141 "transparent P2PKH",
142 )))
143 }
144
145 fn try_from_raw_transparent_p2sh(data: [u8; 20]) -> Result<Self, ConversionError<Self::Error>> {
146 let _ = data;
147 Err(ConversionError::Unsupported(UnsupportedAddress(
148 "transparent P2SH",
149 )))
150 }
151
152 fn try_from_raw_tex(data: [u8; 20]) -> Result<Self, ConversionError<Self::Error>> {
153 let _ = data;
154 Err(ConversionError::Unsupported(UnsupportedAddress(
155 "transparent-source restricted P2PKH",
156 )))
157 }
158}
159
160pub trait TryFromAddress: Sized {
203 type Error;
206
207 fn try_from_sprout(
208 net: NetworkType,
209 data: [u8; 64],
210 ) -> Result<Self, ConversionError<Self::Error>> {
211 let _ = (net, data);
212 Err(ConversionError::Unsupported(UnsupportedAddress("Sprout")))
213 }
214
215 fn try_from_sapling(
216 net: NetworkType,
217 data: [u8; 43],
218 ) -> Result<Self, ConversionError<Self::Error>> {
219 let _ = (net, data);
220 Err(ConversionError::Unsupported(UnsupportedAddress("Sapling")))
221 }
222
223 fn try_from_unified(
224 net: NetworkType,
225 data: unified::Address,
226 ) -> Result<Self, ConversionError<Self::Error>> {
227 let _ = (net, data);
228 Err(ConversionError::Unsupported(UnsupportedAddress("Unified")))
229 }
230
231 fn try_from_transparent_p2pkh(
232 net: NetworkType,
233 data: [u8; 20],
234 ) -> Result<Self, ConversionError<Self::Error>> {
235 let _ = (net, data);
236 Err(ConversionError::Unsupported(UnsupportedAddress(
237 "transparent P2PKH",
238 )))
239 }
240
241 fn try_from_transparent_p2sh(
242 net: NetworkType,
243 data: [u8; 20],
244 ) -> Result<Self, ConversionError<Self::Error>> {
245 let _ = (net, data);
246 Err(ConversionError::Unsupported(UnsupportedAddress(
247 "transparent P2SH",
248 )))
249 }
250
251 fn try_from_tex(
252 net: NetworkType,
253 data: [u8; 20],
254 ) -> Result<Self, ConversionError<Self::Error>> {
255 let _ = (net, data);
256 Err(ConversionError::Unsupported(UnsupportedAddress(
257 "transparent-source restricted P2PKH",
258 )))
259 }
260}
261
262impl<T: TryFromRawAddress> TryFromAddress for (NetworkType, T) {
263 type Error = T::Error;
264
265 fn try_from_sprout(
266 net: NetworkType,
267 data: [u8; 64],
268 ) -> Result<Self, ConversionError<Self::Error>> {
269 T::try_from_raw_sprout(data).map(|addr| (net, addr))
270 }
271
272 fn try_from_sapling(
273 net: NetworkType,
274 data: [u8; 43],
275 ) -> Result<Self, ConversionError<Self::Error>> {
276 T::try_from_raw_sapling(data).map(|addr| (net, addr))
277 }
278
279 fn try_from_unified(
280 net: NetworkType,
281 data: unified::Address,
282 ) -> Result<Self, ConversionError<Self::Error>> {
283 T::try_from_raw_unified(data).map(|addr| (net, addr))
284 }
285
286 fn try_from_transparent_p2pkh(
287 net: NetworkType,
288 data: [u8; 20],
289 ) -> Result<Self, ConversionError<Self::Error>> {
290 T::try_from_raw_transparent_p2pkh(data).map(|addr| (net, addr))
291 }
292
293 fn try_from_transparent_p2sh(
294 net: NetworkType,
295 data: [u8; 20],
296 ) -> Result<Self, ConversionError<Self::Error>> {
297 T::try_from_raw_transparent_p2sh(data).map(|addr| (net, addr))
298 }
299
300 fn try_from_tex(
301 net: NetworkType,
302 data: [u8; 20],
303 ) -> Result<Self, ConversionError<Self::Error>> {
304 T::try_from_raw_tex(data).map(|addr| (net, addr))
305 }
306}
307
308pub trait Converter<T> {
341 type Error;
344
345 fn convert_sprout(
346 &self,
347 net: NetworkType,
348 data: [u8; 64],
349 ) -> Result<T, ConversionError<Self::Error>> {
350 let _ = (net, data);
351 Err(ConversionError::Unsupported(UnsupportedAddress("Sprout")))
352 }
353
354 fn convert_sapling(
355 &self,
356 net: NetworkType,
357 data: [u8; 43],
358 ) -> Result<T, ConversionError<Self::Error>> {
359 let _ = (net, data);
360 Err(ConversionError::Unsupported(UnsupportedAddress("Sapling")))
361 }
362
363 fn convert_unified(
364 &self,
365 net: NetworkType,
366 data: unified::Address,
367 ) -> Result<T, ConversionError<Self::Error>> {
368 let _ = (net, data);
369 Err(ConversionError::Unsupported(UnsupportedAddress("Unified")))
370 }
371
372 fn convert_transparent_p2pkh(
373 &self,
374 net: NetworkType,
375 data: [u8; 20],
376 ) -> Result<T, ConversionError<Self::Error>> {
377 let _ = (net, data);
378 Err(ConversionError::Unsupported(UnsupportedAddress(
379 "transparent P2PKH",
380 )))
381 }
382
383 fn convert_transparent_p2sh(
384 &self,
385 net: NetworkType,
386 data: [u8; 20],
387 ) -> Result<T, ConversionError<Self::Error>> {
388 let _ = (net, data);
389 Err(ConversionError::Unsupported(UnsupportedAddress(
390 "transparent P2SH",
391 )))
392 }
393
394 fn convert_tex(
395 &self,
396 net: NetworkType,
397 data: [u8; 20],
398 ) -> Result<T, ConversionError<Self::Error>> {
399 let _ = (net, data);
400 Err(ConversionError::Unsupported(UnsupportedAddress(
401 "transparent-source restricted P2PKH",
402 )))
403 }
404}
405
406pub trait ToAddress: private::Sealed {
439 fn from_sprout(net: NetworkType, data: [u8; 64]) -> Self;
440
441 fn from_sapling(net: NetworkType, data: [u8; 43]) -> Self;
442
443 fn from_unified(net: NetworkType, data: unified::Address) -> Self;
444
445 fn from_transparent_p2pkh(net: NetworkType, data: [u8; 20]) -> Self;
446
447 fn from_transparent_p2sh(net: NetworkType, data: [u8; 20]) -> Self;
448
449 fn from_tex(net: NetworkType, data: [u8; 20]) -> Self;
450}
451
452impl ToAddress for ZcashAddress {
453 fn from_sprout(net: NetworkType, data: [u8; 64]) -> Self {
454 ZcashAddress {
455 net: if let NetworkType::Regtest = net {
456 NetworkType::Test
457 } else {
458 net
459 },
460 kind: AddressKind::Sprout(data),
461 }
462 }
463
464 fn from_sapling(net: NetworkType, data: [u8; 43]) -> Self {
465 ZcashAddress {
466 net,
467 kind: AddressKind::Sapling(data),
468 }
469 }
470
471 fn from_unified(net: NetworkType, data: unified::Address) -> Self {
472 ZcashAddress {
473 net,
474 kind: AddressKind::Unified(data),
475 }
476 }
477
478 fn from_transparent_p2pkh(net: NetworkType, data: [u8; 20]) -> Self {
479 ZcashAddress {
480 net: if let NetworkType::Regtest = net {
481 NetworkType::Test
482 } else {
483 net
484 },
485 kind: AddressKind::P2pkh(data),
486 }
487 }
488
489 fn from_transparent_p2sh(net: NetworkType, data: [u8; 20]) -> Self {
490 ZcashAddress {
491 net: if let NetworkType::Regtest = net {
492 NetworkType::Test
493 } else {
494 net
495 },
496 kind: AddressKind::P2sh(data),
497 }
498 }
499
500 fn from_tex(net: NetworkType, data: [u8; 20]) -> Self {
501 ZcashAddress {
502 net,
503 kind: AddressKind::Tex(data),
504 }
505 }
506}
507
508mod private {
509 use crate::ZcashAddress;
510
511 pub trait Sealed {}
512 impl Sealed for ZcashAddress {}
513}