|
| 1 | +use bdk::bitcoin::bip32::Fingerprint; |
| 2 | +use bdk::bitcoin::psbt::PartiallySignedTransaction; |
| 3 | +use bdk::bitcoin::secp256k1::{All, Secp256k1}; |
| 4 | + |
| 5 | +use hwi::error::Error; |
| 6 | +use hwi::types::{HWIChain, HWIDevice}; |
| 7 | +use hwi::HWIClient; |
| 8 | + |
| 9 | +use bdk::signer::{SignerCommon, SignerError, SignerId, TransactionSigner}; |
| 10 | + |
| 11 | +#[derive(Debug)] |
| 12 | +/// Custom signer for Hardware Wallets |
| 13 | +/// |
| 14 | +/// This ignores `sign_options` and leaves the decisions up to the hardware wallet. |
| 15 | +pub struct HWISigner { |
| 16 | + fingerprint: Fingerprint, |
| 17 | + client: HWIClient, |
| 18 | +} |
| 19 | + |
| 20 | +impl HWISigner { |
| 21 | + /// Create a instance from the specified device and chain |
| 22 | + pub fn from_device(device: &HWIDevice, chain: HWIChain) -> Result<HWISigner, Error> { |
| 23 | + let client = HWIClient::get_client(device, false, chain)?; |
| 24 | + Ok(HWISigner { |
| 25 | + fingerprint: device.fingerprint, |
| 26 | + client, |
| 27 | + }) |
| 28 | + } |
| 29 | +} |
| 30 | + |
| 31 | +impl SignerCommon for HWISigner { |
| 32 | + fn id(&self, _secp: &Secp256k1<All>) -> SignerId { |
| 33 | + SignerId::Fingerprint(self.fingerprint) |
| 34 | + } |
| 35 | +} |
| 36 | + |
| 37 | +impl TransactionSigner for HWISigner { |
| 38 | + fn sign_transaction( |
| 39 | + &self, |
| 40 | + psbt: &mut PartiallySignedTransaction, |
| 41 | + _sign_options: &bdk::SignOptions, |
| 42 | + _secp: &Secp256k1<All>, |
| 43 | + ) -> Result<(), SignerError> { |
| 44 | + psbt.combine( |
| 45 | + self.client |
| 46 | + .sign_tx(psbt) |
| 47 | + .map_err(|e| { |
| 48 | + SignerError::External(format!("While signing with hardware wallet: {}", e)) |
| 49 | + })? |
| 50 | + .psbt, |
| 51 | + ) |
| 52 | + .expect("Failed to combine HW signed psbt with passed PSBT"); |
| 53 | + Ok(()) |
| 54 | + } |
| 55 | +} |
| 56 | + |
| 57 | +// TODO: re-enable this once we have the `get_funded_wallet` test util |
| 58 | +// #[cfg(test)] |
| 59 | +// mod tests { |
| 60 | +// #[test] |
| 61 | +// fn test_hardware_signer() { |
| 62 | +// use std::sync::Arc; |
| 63 | +// |
| 64 | +// use bdk::tests::get_funded_wallet; |
| 65 | +// use bdk::signer::SignerOrdering; |
| 66 | +// use bdk::bitcoin::Network; |
| 67 | +// use crate::HWISigner; |
| 68 | +// use hwi::HWIClient; |
| 69 | +// |
| 70 | +// let mut devices = HWIClient::enumerate().unwrap(); |
| 71 | +// if devices.is_empty() { |
| 72 | +// panic!("No devices found!"); |
| 73 | +// } |
| 74 | +// let device = devices.remove(0).unwrap(); |
| 75 | +// let client = HWIClient::get_client(&device, true, Network::Regtest.into()).unwrap(); |
| 76 | +// let descriptors = client.get_descriptors::<String>(None).unwrap(); |
| 77 | +// let custom_signer = HWISigner::from_device(&device, Network::Regtest.into()).unwrap(); |
| 78 | +// |
| 79 | +// let (mut wallet, _) = get_funded_wallet(&descriptors.internal[0]); |
| 80 | +// wallet.add_signer( |
| 81 | +// bdk::KeychainKind::External, |
| 82 | +// SignerOrdering(200), |
| 83 | +// Arc::new(custom_signer), |
| 84 | +// ); |
| 85 | +// |
| 86 | +// let addr = wallet.get_address(bdk::wallet::AddressIndex::LastUnused); |
| 87 | +// let mut builder = wallet.build_tx(); |
| 88 | +// builder.drain_to(addr.script_pubkey()).drain_wallet(); |
| 89 | +// let (mut psbt, _) = builder.finish().unwrap(); |
| 90 | +// |
| 91 | +// let finalized = wallet.sign(&mut psbt, Default::default()).unwrap(); |
| 92 | +// assert!(finalized); |
| 93 | +// } |
| 94 | +// } |
0 commit comments