hwi 0.2.0

Rust wrapper for the Bitcoin Core Hardware Wallet Interface.
Documentation
use std::ops::Deref;

use bitcoin::util::bip32::{ExtendedPubKey, Fingerprint};
use bitcoin::util::{address::Address, psbt::PartiallySignedTransaction};

use pyo3::types::PyModule;
use pyo3::{IntoPy, PyObject};
use serde::{Deserialize, Deserializer};

use crate::error::Error;

#[derive(Clone, Eq, PartialEq, Debug, Deserialize)]
pub struct HWIExtendedPubKey {
    pub xpub: ExtendedPubKey,
}

impl Deref for HWIExtendedPubKey {
    type Target = ExtendedPubKey;

    fn deref(&self) -> &Self::Target {
        &self.xpub
    }
}

#[derive(Clone, Eq, PartialEq, Debug, Deserialize)]
pub struct HWISignature {
    #[serde(deserialize_with = "from_b64")]
    pub signature: Vec<u8>,
}

fn from_b64<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
    let b64_string = String::deserialize(d)?;
    bitcoin::base64::decode(&b64_string)
        .map_err(|_| serde::de::Error::custom("Error while Deserializing Signature"))
}

impl Deref for HWISignature {
    type Target = Vec<u8>;

    fn deref(&self) -> &Self::Target {
        &self.signature
    }
}

#[derive(Clone, Eq, PartialEq, Debug, Deserialize)]
pub struct HWIAddress {
    pub address: Address,
}

#[derive(Clone, Eq, PartialEq, Debug, Deserialize)]
pub struct HWIPartiallySignedTransaction {
    pub psbt: PartiallySignedTransaction,
}

impl Deref for HWIPartiallySignedTransaction {
    type Target = PartiallySignedTransaction;

    fn deref(&self) -> &Self::Target {
        &self.psbt
    }
}

// TODO: use Descriptors
#[derive(Clone, Eq, PartialEq, Debug, Deserialize)]
pub struct HWIDescriptor {
    pub internal: Vec<String>,
    pub receive: Vec<String>,
}

#[derive(Clone, Eq, PartialEq, Debug, Deserialize)]
pub struct HWIKeyPoolElement {
    pub desc: String,
    pub range: Vec<u32>,
    pub timestamp: String,
    pub internal: bool,
    pub keypool: bool,
    pub watchonly: bool,
}

#[derive(Clone, Eq, PartialEq, Debug, Deserialize)]
#[allow(non_camel_case_types)]
pub enum HWIAddressType {
    Legacy,
    Sh_Wit,
    Wit,
    Tap,
}

impl IntoPy<PyObject> for HWIAddressType {
    fn into_py(self, py: pyo3::Python) -> PyObject {
        let addrtype = PyModule::import(py, "hwilib.common")
            .unwrap()
            .getattr("AddressType")
            .unwrap();
        match self {
            HWIAddressType::Legacy => addrtype.get_item("LEGACY").unwrap().into(),
            HWIAddressType::Sh_Wit => addrtype.get_item("SH_WIT").unwrap().into(),
            HWIAddressType::Wit => addrtype.get_item("WIT").unwrap().into(),
            HWIAddressType::Tap => addrtype.get_item("TAP").unwrap().into(),
        }
    }
}

#[derive(Clone, Eq, PartialEq, Debug, Deserialize)]
pub enum HWIChain {
    Main,
    Test,
    Regtest,
    Signet,
}
impl IntoPy<PyObject> for HWIChain {
    fn into_py(self, py: pyo3::Python) -> PyObject {
        let chain = PyModule::import(py, "hwilib.common")
            .unwrap()
            .getattr("Chain")
            .unwrap();
        match self {
            HWIChain::Main => chain.get_item("MAIN").unwrap().into(),
            HWIChain::Test => chain.get_item("TEST").unwrap().into(),
            HWIChain::Regtest => chain.get_item("REGTEST").unwrap().into(),
            HWIChain::Signet => chain.get_item("SIGNET").unwrap().into(),
        }
    }
}

#[derive(Clone, Eq, PartialEq, Debug, Deserialize)]
pub struct HWIDevice {
    #[serde(rename(deserialize = "type"))]
    pub device_type: String,
    pub model: String,
    pub path: String,
    pub needs_pin_sent: bool,
    pub needs_passphrase_sent: bool,
    pub fingerprint: Fingerprint,
}

#[derive(Clone, Eq, PartialEq, Debug, Deserialize)]
pub struct HWIStatus {
    pub success: bool,
}

impl From<HWIStatus> for Result<(), Error> {
    fn from(s: HWIStatus) -> Self {
        if s.success {
            Ok(())
        } else {
            Err(Error::HWIError("Request returned with failure".to_string()))
        }
    }
}
pub enum LogLevel {
    DEBUG,
    INFO,
    WARNING,
    ERROR,
    CRITICAL,
}