use core::str::FromStr; use scale::{Encode, Decode}; use bitcoin::{ hashes::{Hash as HashTrait, hash160::Hash}, PubkeyHash, ScriptHash, network::constants::Network, util::address::{Error, WitnessVersion, Payload, Address as BAddress}, }; #[derive(Clone, PartialEq, Eq, Debug)] pub struct Address(pub BAddress); impl FromStr for Address { type Err = Error; fn from_str(str: &str) -> Result { BAddress::from_str(str).map(Address) } } impl ToString for Address { fn to_string(&self) -> String { self.0.to_string() } } // SCALE-encoded variant of Monero addresses. #[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)] enum EncodedAddress { P2PKH([u8; 20]), P2SH([u8; 20]), P2WPKH([u8; 20]), P2WSH([u8; 32]), P2TR([u8; 32]), } impl TryFrom> for Address { type Error = (); fn try_from(data: Vec) -> Result { Ok(Address(BAddress { network: Network::Bitcoin, payload: match EncodedAddress::decode(&mut data.as_ref()).map_err(|_| ())? { EncodedAddress::P2PKH(hash) => { Payload::PubkeyHash(PubkeyHash::from_hash(Hash::from_inner(hash))) } EncodedAddress::P2SH(hash) => { Payload::ScriptHash(ScriptHash::from_hash(Hash::from_inner(hash))) } EncodedAddress::P2WPKH(hash) => { Payload::WitnessProgram { version: WitnessVersion::V0, program: hash.to_vec() } } EncodedAddress::P2WSH(hash) => { Payload::WitnessProgram { version: WitnessVersion::V0, program: hash.to_vec() } } EncodedAddress::P2TR(key) => { Payload::WitnessProgram { version: WitnessVersion::V1, program: key.to_vec() } } }, })) } } #[allow(clippy::from_over_into)] impl TryInto> for Address { type Error = (); fn try_into(self) -> Result, ()> { Ok( (match self.0.payload { Payload::PubkeyHash(hash) => EncodedAddress::P2PKH(hash.as_hash().into_inner()), Payload::ScriptHash(hash) => EncodedAddress::P2SH(hash.as_hash().into_inner()), Payload::WitnessProgram { version: WitnessVersion::V0, program } => { if program.len() == 20 { EncodedAddress::P2WPKH(program.try_into().map_err(|_| ())?) } else if program.len() == 32 { EncodedAddress::P2WSH(program.try_into().map_err(|_| ())?) } else { Err(())? } } Payload::WitnessProgram { version: WitnessVersion::V1, program } => { EncodedAddress::P2TR(program.try_into().map_err(|_| ())?) } _ => Err(())?, }) .encode(), ) } }