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}