use zcash_address::unified::{self, Container};
use crate::{parameters::NetworkKind, transparent, BoxError};
pub enum Address {
Transparent(transparent::Address),
Sapling {
network: NetworkKind,
address: sapling_crypto::PaymentAddress,
},
Unified {
network: NetworkKind,
unified_address: zcash_address::unified::Address,
orchard: Option<orchard::Address>,
sapling: Option<sapling_crypto::PaymentAddress>,
transparent: Option<transparent::Address>,
},
}
impl zcash_address::TryFromAddress for Address {
type Error = BoxError;
fn try_from_transparent_p2pkh(
network: zcash_address::Network,
data: [u8; 20],
) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
Ok(Self::Transparent(transparent::Address::from_pub_key_hash(
network.into(),
data,
)))
}
fn try_from_transparent_p2sh(
network: zcash_address::Network,
data: [u8; 20],
) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
Ok(Self::Transparent(transparent::Address::from_script_hash(
network.into(),
data,
)))
}
fn try_from_sapling(
network: zcash_address::Network,
data: [u8; 43],
) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
let network = network.into();
sapling_crypto::PaymentAddress::from_bytes(&data)
.map(|address| Self::Sapling { address, network })
.ok_or_else(|| BoxError::from("not a valid sapling address").into())
}
fn try_from_unified(
network: zcash_address::Network,
unified_address: zcash_address::unified::Address,
) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
let network = network.into();
let mut orchard = None;
let mut sapling = None;
let mut transparent = None;
for receiver in unified_address.items().into_iter() {
match receiver {
unified::Receiver::Orchard(data) => {
orchard = orchard::Address::from_raw_address_bytes(&data).into();
if orchard.is_none() {
return Err(BoxError::from(
"Unified Address contains an invalid Orchard receiver.",
)
.into());
}
}
unified::Receiver::Sapling(data) => {
sapling = sapling_crypto::PaymentAddress::from_bytes(&data);
if sapling.is_none() {
return Err(BoxError::from(
"Unified Address contains an invalid Sapling receiver",
)
.into());
}
}
unified::Receiver::P2pkh(data) => {
transparent = Some(transparent::Address::from_pub_key_hash(network, data));
}
unified::Receiver::P2sh(data) => {
transparent = Some(transparent::Address::from_script_hash(network, data));
}
unified::Receiver::Unknown { .. } => {
return Err(BoxError::from("Unsupported receiver in a Unified Address.").into());
}
}
}
Ok(Self::Unified {
network,
unified_address,
orchard,
sapling,
transparent,
})
}
}
impl Address {
pub fn network(&self) -> NetworkKind {
match &self {
Self::Transparent(address) => address.network_kind(),
Self::Sapling { network, .. } | Self::Unified { network, .. } => *network,
}
}
pub fn is_script_hash(&self) -> bool {
match &self {
Self::Transparent(address) => address.is_script_hash(),
Self::Sapling { .. } | Self::Unified { .. } => false,
}
}
pub fn is_transparent(&self) -> bool {
matches!(self, Self::Transparent(_))
}
pub fn payment_address(&self) -> Option<String> {
use zcash_address::{ToAddress, ZcashAddress};
match &self {
Self::Transparent(address) => Some(address.to_string()),
Self::Sapling { address, network } => {
let data = address.to_bytes();
let address = ZcashAddress::from_sapling(network.into(), data);
Some(address.encode())
}
Self::Unified { .. } => None,
}
}
}
impl From<zcash_address::Network> for NetworkKind {
fn from(network: zcash_address::Network) -> Self {
match network {
zcash_address::Network::Main => NetworkKind::Mainnet,
zcash_address::Network::Test => NetworkKind::Testnet,
zcash_address::Network::Regtest => NetworkKind::Regtest,
}
}
}
impl From<NetworkKind> for zcash_address::Network {
fn from(network: NetworkKind) -> Self {
match network {
NetworkKind::Mainnet => zcash_address::Network::Main,
NetworkKind::Testnet => zcash_address::Network::Test,
NetworkKind::Regtest => zcash_address::Network::Regtest,
}
}
}
impl From<&NetworkKind> for zcash_address::Network {
fn from(network: &NetworkKind) -> Self {
(*network).into()
}
}