use core::fmt::{self, Display};
use core::str::FromStr;
use bitcoin::bip32;
use super::WalletPolicyError;
use crate::descriptor::key::maybe_fmt_master_id;
use crate::descriptor::{DescriptorKeyParseError, DescriptorXKey, Wildcard};
use crate::{DescriptorPublicKey, ToString};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct KeyInfo {
pub origin: Option<(bip32::Fingerprint, bip32::DerivationPath)>,
pub xkey: bip32::Xpub,
}
impl TryFrom<DescriptorPublicKey> for KeyInfo {
type Error = WalletPolicyError;
fn try_from(pk: DescriptorPublicKey) -> Result<Self, Self::Error> {
match &pk {
DescriptorPublicKey::XPub(xpub)
if xpub.derivation_path.is_empty() && xpub.wildcard == Wildcard::None =>
{
to_key_info(&pk)
}
DescriptorPublicKey::XPub(_) | DescriptorPublicKey::MultiXPub(_) => {
Err(WalletPolicyError::KeyInfoUnexpectedDerivation(pk.to_string()))
}
DescriptorPublicKey::Single(_) => Err(WalletPolicyError::KeyInfoNotExtendedKey),
}
}
}
impl From<KeyInfo> for DescriptorPublicKey {
fn from(key: KeyInfo) -> Self {
Self::XPub(DescriptorXKey {
origin: key.origin,
xkey: key.xkey,
derivation_path: bip32::DerivationPath::master(),
wildcard: Wildcard::None,
})
}
}
impl FromStr for KeyInfo {
type Err = DescriptorKeyParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from(DescriptorPublicKey::from_str(s)?).map_err(Into::into)
}
}
impl Display for KeyInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
maybe_fmt_master_id(f, &self.origin)?;
self.xkey.fmt(f)
}
}
pub(super) fn to_key_info(pk: &DescriptorPublicKey) -> Result<KeyInfo, WalletPolicyError> {
let (origin, xkey) = match pk {
DescriptorPublicKey::XPub(xpub) => (xpub.origin.clone(), xpub.xkey),
DescriptorPublicKey::MultiXPub(xpub) => (xpub.origin.clone(), xpub.xkey),
DescriptorPublicKey::Single(_) => return Err(WalletPolicyError::KeyInfoNotExtendedKey),
};
Ok(KeyInfo { origin, xkey })
}