From a366460157f600c81814eba0c184bd4cc6bce764 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 8 Mar 2021 04:46:19 +0000 Subject: [PATCH] zcash_address: ZcashAddress::convert() -> T: FromAddress This enables easy conversion of an encoded Zcash address to a target type, with automatic handling of Zcash address types that are not supported by the target. --- components/zcash_address/src/convert.rs | 86 +++++++++++++++++++++++++ components/zcash_address/src/lib.rs | 4 +- 2 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 components/zcash_address/src/convert.rs diff --git a/components/zcash_address/src/convert.rs b/components/zcash_address/src/convert.rs new file mode 100644 index 000000000..50a500ad4 --- /dev/null +++ b/components/zcash_address/src/convert.rs @@ -0,0 +1,86 @@ +use std::{error::Error, fmt}; + +use crate::{kind::*, AddressKind, Network, ZcashAddress}; + +/// An address type is not supported for conversion. +#[derive(Debug)] +pub struct UnsupportedAddress(&'static str); + +impl fmt::Display for UnsupportedAddress { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Zcash {} addresses are not supported", self.0) + } +} + +impl Error for UnsupportedAddress {} + +impl ZcashAddress { + pub fn convert(self) -> Result { + match self.kind { + AddressKind::Sprout(data) => T::from_sprout(self.net, data), + AddressKind::Sapling(data) => T::from_sapling(self.net, data), + AddressKind::Orchard(data) => T::from_orchard(self.net, data), + AddressKind::P2pkh(data) => T::from_transparent_p2pkh(self.net, data), + AddressKind::P2sh(data) => T::from_transparent_p2sh(self.net, data), + } + } +} + +/// A helper trait for converting a [`ZcashAddress`] into another type. +/// +/// # Examples +/// +/// ``` +/// use zcash_address::{FromAddress, Network, UnsupportedAddress, ZcashAddress}; +/// +/// #[derive(Debug)] +/// struct MySapling([u8; 43]); +/// +/// // Implement the FromAddress trait, overriding whichever conversion methods match your +/// // requirements for the resulting type. +/// impl FromAddress for MySapling { +/// fn from_sapling(net: Network, data: [u8; 43]) -> Result { +/// Ok(MySapling(data)) +/// } +/// } +/// +/// // For a supported address type, the conversion works. +/// let addr: ZcashAddress = +/// "zs1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpq6d8g" +/// .parse() +/// .unwrap(); +/// assert!(addr.convert::().is_ok()); +/// +/// // For an unsupported address type, we get an error. +/// let addr: ZcashAddress = "t1Hsc1LR8yKnbbe3twRp88p6vFfC5t7DLbs".parse().unwrap(); +/// assert_eq!( +/// addr.convert::().unwrap_err().to_string(), +/// "Zcash transparent P2PKH addresses are not supported", +/// ); +/// ``` +pub trait FromAddress: Sized { + fn from_sprout(net: Network, data: sprout::Data) -> Result { + let _ = (net, data); + Err(UnsupportedAddress("Sprout")) + } + + fn from_sapling(net: Network, data: sapling::Data) -> Result { + let _ = (net, data); + Err(UnsupportedAddress("Sapling")) + } + + fn from_orchard(net: Network, data: orchard::Data) -> Result { + let _ = (net, data); + Err(UnsupportedAddress("Orchard")) + } + + fn from_transparent_p2pkh(net: Network, data: p2pkh::Data) -> Result { + let _ = (net, data); + Err(UnsupportedAddress("transparent P2PKH")) + } + + fn from_transparent_p2sh(net: Network, data: p2sh::Data) -> Result { + let _ = (net, data); + Err(UnsupportedAddress("transparent P2SH")) + } +} diff --git a/components/zcash_address/src/lib.rs b/components/zcash_address/src/lib.rs index 3a01029e7..76139aca1 100644 --- a/components/zcash_address/src/lib.rs +++ b/components/zcash_address/src/lib.rs @@ -1,6 +1,8 @@ +mod convert; mod encoding; mod kind; +pub use convert::{FromAddress, UnsupportedAddress}; pub use encoding::ParseError; /// A Zcash address. @@ -12,7 +14,7 @@ pub struct ZcashAddress { /// The Zcash network for which an address is encoded. #[derive(Debug, PartialEq)] -enum Network { +pub enum Network { /// Zcash Mainnet. Main, /// Zcash Testnet.