bip352 0.1.0-alpha.3

Implementation of BIP 352 Silent Payments
Documentation
use std::collections::HashMap;

use bitcoin::bip32::{DerivationPath, ExtendedPrivKey, Fingerprint};
use bitcoin::secp256k1::{All, Secp256k1, SecretKey};
use miniscript::descriptor::{DescriptorSecretKey, ShInner};
use miniscript::{Descriptor, DescriptorPublicKey};
use serde::Deserialize;

#[derive(Debug, Deserialize)]
pub struct ListDescriptorsResult {
    pub wallet_name: String,
    pub descriptors: Vec<ListDescriptorsDescriptor>,
}

#[derive(Debug, Deserialize)]
pub struct ListDescriptorsDescriptor {
    desc: String,
}

#[derive(Debug, Deserialize)]
pub struct GetAddress {
    pub desc: String,
}

pub struct PrivateKeys<'a> {
    pub keys: HashMap<Fingerprint, ExtendedPrivKey>,
    pub secp: &'a Secp256k1<All>,
}

impl<'a> PrivateKeys<'a> {
    pub fn for_descriptor(&self, desc: &Descriptor<DescriptorPublicKey>) -> Option<SecretKey> {
        let origin = extract_origin(desc)?;
        let xpriv = self.keys.get(&origin.0)?;
        let x = xpriv.derive_priv(self.secp, &origin.1).ok()?;
        Some(x.private_key)
    }
}

pub fn collect_xprivs(
    secp: &Secp256k1<All>,
    descs: &[ListDescriptorsDescriptor],
) -> HashMap<Fingerprint, ExtendedPrivKey> {
    descs
        .iter()
        .filter_map(|d| Descriptor::parse_descriptor(secp, &d.desc).ok())
        .flat_map(|x| x.1.values().cloned().collect::<Vec<_>>())
        .filter_map(|x| match x {
            DescriptorSecretKey::XPrv(x) => Some(x.xkey),
            _ => None,
        })
        .map(|x| (x.fingerprint(secp), x))
        .collect::<HashMap<_, _>>()
}

pub fn extract_origin(
    desc: &Descriptor<DescriptorPublicKey>,
) -> Option<&(Fingerprint, DerivationPath)> {
    fn get_origin(pk: &DescriptorPublicKey) -> Option<&(Fingerprint, DerivationPath)> {
        match pk {
            DescriptorPublicKey::Single(s) => s.origin.as_ref(),
            DescriptorPublicKey::XPub(x) => x.origin.as_ref(),
            DescriptorPublicKey::MultiXPub(_) => None,
        }
    }

    match desc {
        Descriptor::Bare(_) => None,
        Descriptor::Pkh(p) => get_origin(p.as_inner()),
        Descriptor::Wpkh(w) => get_origin(w.as_inner()),
        Descriptor::Sh(s) => match s.as_inner() {
            ShInner::Wpkh(w) => get_origin(w.as_inner()),
            _ => None,
        },
        Descriptor::Wsh(_) => None,
        Descriptor::Tr(tr) => get_origin(tr.internal_key()),
    }
}