use std::convert::TryFrom;
use std::ops::Deref;
use bitcoin::util::bip32::{ExtendedPubKey, Fingerprint};
use bitcoin::util::{address::Address, psbt::PartiallySignedTransaction};
use bitcoin::Network;
use pyo3::types::PyModule;
use pyo3::{IntoPy, PyObject};
use serde::{Deserialize, Deserializer};
#[cfg(feature = "use-miniscript")]
use miniscript::{Descriptor, DescriptorPublicKey};
use crate::error::{Error, ErrorCode};
#[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)?;
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 {
#[serde(deserialize_with = "deserialize_psbt")]
pub psbt: PartiallySignedTransaction,
}
fn deserialize_psbt<'de, D: Deserializer<'de>>(
d: D,
) -> Result<PartiallySignedTransaction, D::Error> {
let b64_string = String::deserialize(d)?;
let bytes = base64::decode(b64_string).map_err(serde::de::Error::custom)?;
bitcoin::consensus::deserialize::<PartiallySignedTransaction>(&bytes[..])
.map_err(serde::de::Error::custom)
}
impl Deref for HWIPartiallySignedTransaction {
type Target = PartiallySignedTransaction;
fn deref(&self) -> &Self::Target {
&self.psbt
}
}
pub trait ToDescriptor {}
impl ToDescriptor for String {}
#[cfg(feature = "use-miniscript")]
impl ToDescriptor for Descriptor<DescriptorPublicKey> {}
#[derive(Clone, Eq, PartialEq, Debug, Deserialize)]
pub struct HWIDescriptor<T>
where
T: ToDescriptor,
{
pub internal: Vec<T>,
pub receive: Vec<T>,
}
#[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(),
}
}
}
impl From<Network> for HWIChain {
fn from(network: Network) -> Self {
match network {
Network::Bitcoin => Self::Main,
Network::Testnet => Self::Test,
Network::Regtest => Self::Regtest,
Network::Signet => Self::Signet,
}
}
}
#[derive(Clone, Eq, PartialEq, Debug, Deserialize)]
pub(crate) struct HWIDeviceInternal {
#[serde(rename(deserialize = "type"))]
pub device_type: Option<String>,
pub model: Option<String>,
pub path: Option<String>,
pub needs_pin_sent: Option<bool>,
pub needs_passphrase_sent: Option<bool>,
pub fingerprint: Option<Fingerprint>,
pub error: Option<String>,
pub code: Option<i8>,
}
#[derive(Clone, Eq, PartialEq, Debug, Deserialize)]
pub struct HWIDevice {
#[serde(rename(deserialize = "type"))]
pub device_type: HWIDeviceType,
pub model: String,
pub path: String,
pub needs_pin_sent: bool,
pub needs_passphrase_sent: bool,
pub fingerprint: Fingerprint,
}
impl TryFrom<HWIDeviceInternal> for HWIDevice {
type Error = Error;
fn try_from(h: HWIDeviceInternal) -> Result<HWIDevice, Error> {
match h.error {
Some(e) => {
let code = h.code.and_then(|c| ErrorCode::try_from(c).ok());
Err(Error::HWIError(e, code))
}
None => Ok(HWIDevice {
device_type: HWIDeviceType::from(
h.device_type.expect("Device type should be here"),
),
model: h.model.expect("Model should be here"),
path: h.path.expect("Path should be here"),
needs_pin_sent: h.needs_pin_sent.expect("needs_pin_sent should be here"),
needs_passphrase_sent: h
.needs_passphrase_sent
.expect("needs_passphrase_sent should be here"),
fingerprint: h.fingerprint.expect("Fingerprint should be here"),
}),
}
}
}
#[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(),
None,
))
}
}
}
#[derive(Clone, Eq, PartialEq, Debug, Deserialize)]
pub enum HWIDeviceType {
Ledger,
Trezor,
BitBox01,
BitBox02,
KeepKey,
Coldcard,
Jade,
Other(String),
}
impl<T> From<T> for HWIDeviceType
where
T: AsRef<str>,
{
fn from(s: T) -> Self {
match s.as_ref() {
"ledger" => Self::Ledger,
"trezor" => Self::Trezor,
"digitalbitbox" => Self::BitBox01,
"bitbox02" => Self::BitBox02,
"keepkey" => Self::KeepKey,
"coldcard" => Self::Coldcard,
"jade" => Self::Jade,
name => Self::Other(name.to_string()),
}
}
}
impl ToString for HWIDeviceType {
fn to_string(&self) -> String {
match self {
Self::Ledger => String::from("ledger"),
Self::Trezor => String::from("trezor"),
Self::BitBox01 => String::from("digitalbitbox"),
Self::BitBox02 => String::from("bitbox02"),
Self::KeepKey => String::from("keepkey"),
Self::Coldcard => String::from("coldcard"),
Self::Jade => String::from("jade"),
Self::Other(name) => name.to_string(),
}
}
}
pub enum LogLevel {
DEBUG,
INFO,
WARNING,
ERROR,
CRITICAL,
}
#[derive(Clone, Eq, PartialEq, Debug, Copy)]
#[repr(u8)]
pub enum HWIWordCount {
W12 = 12,
W18 = 18,
W24 = 24,
}