/// Seems like quite a lot of code just to pullout `from_bech32` on address /// This is mostly extracted from private function of pallas_addresses use anyhow::{Result, anyhow}; use pallas_addresses; pub use pallas_addresses::Network; use pallas_primitives::NetworkId; use crate::utils::v2a; /// Network pub fn network_from_id(id: NetworkId) -> Network { match id { NetworkId::Testnet => Network::Testnet, NetworkId::Mainnet => Network::Mainnet, } } fn parse_network(header: u8) -> Network { let masked = header & 0b0000_1111; match masked { 0b_0000_0000 => Network::Testnet, 0b_0000_0001 => Network::Mainnet, _ => Network::Other(masked), } } /// Credential #[derive(Debug, PartialEq, Clone)] pub enum Credential { Key([u8; 28]), Script([u8; 28]), } impl Into for Credential { fn into(self) -> pallas_addresses::ShelleyPaymentPart { match self { Credential::Key(hash) => pallas_addresses::ShelleyPaymentPart::Key(hash.into()), Credential::Script(hash) => pallas_addresses::ShelleyPaymentPart::Script(hash.into()), } } } impl Into for Credential { fn into(self) -> pallas_addresses::ShelleyDelegationPart { match self { Credential::Key(hash) => pallas_addresses::ShelleyDelegationPart::Key(hash.into()), Credential::Script(hash) => { pallas_addresses::ShelleyDelegationPart::Script(hash.into()) } } } } /// Address #[derive(Debug)] pub struct Address { pub network: Network, pub payment: Credential, pub delegation: Option, } impl Address { pub fn new(network: Network, payment: Credential, delegation: Option) -> Self { Self { network, payment, delegation, } } pub fn from_bech32(s: &str) -> Result { let (_, bytes) = bech32::decode(s)?; let x = Address::try_from(bytes)?; Ok(x) } pub fn to_bech32(self) -> String { Into::::into(self) .to_bech32() .unwrap() } } impl TryFrom> for Address { fn try_from(bytes: Vec) -> std::result::Result { let header = *bytes.first().expect("Missing bytes"); let network = parse_network(header); let payment: [u8; 28] = v2a(bytes[1..=28].to_vec())?.into(); let delegation = v2a(bytes[28..].to_vec()); match header & 0b1111_0000 { 0b0000_0000 => Ok(Address::new( network, Credential::Key(payment), Some(Credential::Key(delegation?.into())), )), 0b0001_0000 => Ok(Address::new( network, Credential::Script(payment), Some(Credential::Key(delegation?.into())), )), 0b0010_0000 => Ok(Address::new( network, Credential::Key(payment), Some(Credential::Script(delegation?.into())), )), 0b0011_0000 => Ok(Address::new( network, Credential::Script(payment), Some(Credential::Script(delegation?.into())), )), 0b0110_0000 => Ok(Address::new(network, Credential::Key(payment), None)), 0b0111_0000 => Ok(Address::new(network, Credential::Script(payment), None)), _ => Err(anyhow!("Header not supported")), } } type Error = anyhow::Error; } impl Into for Address { fn into(self) -> pallas_addresses::ShelleyAddress { let network = self.network; pallas_addresses::ShelleyAddress::new( network, self.payment.into(), self.delegation .map_or(pallas_addresses::ShelleyDelegationPart::Null, |x| x.into()), ) } } #[cfg(test)] mod tests { use super::*; #[test] fn parse_bech32() { let testnet = "addr_test1vqpymjsk49r6p9f2f6d3gm04ther0ka7ts8jsv8dukg4nvgcdjxy9"; let addr0 = Address::from_bech32(testnet).unwrap(); let mainnet = "addr1vypymjsk49r6p9f2f6d3gm04ther0ka7ts8jsv8dukg4nvgr9x6tq"; let addr1 = Address::from_bech32(mainnet).unwrap(); assert_eq!(addr0.delegation, addr1.delegation, "oops"); let sa0: pallas_addresses::ShelleyAddress = addr0.into(); assert_eq!(sa0.to_bech32().unwrap(), testnet, "oops"); let sa1: pallas_addresses::ShelleyAddress = addr1.into(); assert_eq!(sa1.to_bech32().unwrap(), mainnet, "oops"); } }