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