Skip to content

Commit

Permalink
feat: add HWISigner, moved from bdk_hwi
Browse files Browse the repository at this point in the history
- adds a new `signer.rs` that contains the previous implementation of
  `HWISigner`, which implements `bdk_wallet::signer::{SignerCommon,
TransactionSigner}` traits.
- expose the new `signer::HWISigner` as public.
- updates the crate documentation.
- TODO: re-add test that relies on `bdk_wallet::tests::common::get_funded_wallet`
  helper methods.
  • Loading branch information
oleonardolima committed Aug 16, 2024
1 parent 5e88904 commit b25168a
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 3 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ readme = "README.md"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bdk_wallet = { version = "1.0.0-beta.1" }
bitcoin = { version = "0.32", features = ["serde", "base64"] }
pyo3 = { version = "0.21.2", features = ["auto-initialize"] }
serde = { version = "^1.0", features = ["derive"] }
serde_json = { version = "^1.0" }
pyo3 = { version = "0.21.2", features = ["auto-initialize"] }

miniscript = { version = "12.0", features = ["serde"], optional = true }

Expand Down
43 changes: 41 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! Rust wrapper for the [Bitcoin Hardware Wallet Interface](https://github.com/bitcoin-core/HWI/).
//! This crate is contains both:
//! - [`HWIClient`]: A Rust wrapper for the [Bitcoin Hardware Wallet Interface](https://github.com/bitcoin-core/HWI/).
//! - [`HWISigner`]: An implementation of a [`TransactionSigner`] to be used with hardware wallets, that relies on [`HWIClient`].
//!
//! # Example - display address with path
//! # HWIClient Example:
//! ## Display address with path
//! ```no_run
//! use bitcoin::bip32::{ChildNumber, DerivationPath};
//! use hwi::error::Error;
Expand All @@ -25,18 +28,54 @@
//! Ok(())
//! }
//! ```
//!
//! # HWISigner Example:
//! ## Add custom HWI signer to [`bdk_wallet`]
//! ```no_run
//! # use bdk_wallet::bitcoin::Network;
//! # use bdk_wallet::descriptor::Descriptor;
//! # use bdk_wallet::signer::SignerOrdering;
//! # use hwi::{HWIClient, HWISigner};
//! # use bdk_wallet::{KeychainKind, SignOptions, Wallet};
//! # use std::sync::Arc;
//! # use std::str::FromStr;
//! #
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let mut devices = HWIClient::enumerate()?;
//! if devices.is_empty() {
//! panic!("No devices found!");
//! }
//! let first_device = devices.remove(0)?;
//! let custom_signer = HWISigner::from_device(&first_device, Network::Testnet.into())?;
//!
//! # let mut wallet = Wallet::create("", "").network(Network::Testnet).create_wallet_no_persist()?;
//! #
//! // Adding the hardware signer to the BDK wallet
//! wallet.add_signer(
//! KeychainKind::External,
//! SignerOrdering(200),
//! Arc::new(custom_signer),
//! );
//!
//! # Ok(())
//! # }
//! ```
//!
//! [`TransactionSigner`]: bdk_wallet::signer::TransactionSigner

#[cfg(test)]
#[macro_use]
extern crate serial_test;
extern crate core;

pub use interface::HWIClient;
pub use signer::HWISigner;

#[cfg(feature = "doctest")]
pub mod doctest;
pub mod error;
pub mod interface;
pub mod signer;
pub mod types;

#[cfg(test)]
Expand Down
55 changes: 55 additions & 0 deletions src/signer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use bdk_wallet::bitcoin::bip32::Fingerprint;
use bdk_wallet::bitcoin::secp256k1::{All, Secp256k1};
use bdk_wallet::bitcoin::Psbt;

use crate::error::Error;
use crate::types::{HWIChain, HWIDevice};
use crate::HWIClient;

use bdk_wallet::signer::{SignerCommon, SignerError, SignerId, TransactionSigner};

#[derive(Debug)]
/// Custom signer for Hardware Wallets
///
/// This ignores `sign_options` and leaves the decisions up to the hardware wallet.
pub struct HWISigner {
fingerprint: Fingerprint,
client: HWIClient,
}

impl HWISigner {
/// Create an instance from the specified device and chain
pub fn from_device(device: &HWIDevice, chain: HWIChain) -> Result<HWISigner, Error> {
let client = HWIClient::get_client(device, false, chain)?;
Ok(HWISigner {
fingerprint: device.fingerprint,
client,
})
}
}

impl SignerCommon for HWISigner {
fn id(&self, _secp: &Secp256k1<All>) -> SignerId {
SignerId::Fingerprint(self.fingerprint)
}
}

impl TransactionSigner for HWISigner {
fn sign_transaction(
&self,
psbt: &mut Psbt,
_sign_options: &bdk_wallet::SignOptions,
_secp: &Secp256k1<All>,
) -> Result<(), SignerError> {
psbt.combine(
self.client
.sign_tx(psbt)
.map_err(|e| {
SignerError::External(format!("While signing with hardware wallet: {}", e))
})?
.psbt,
)
.expect("Failed to combine HW signed psbt with passed PSBT");
Ok(())
}
}

0 comments on commit b25168a

Please sign in to comment.