## [Unreleased]
- Add capacity to create FeeRate from sats/kvbytes and sats/kwu.
- Rename `as_sat_vb` to `as_sat_per_vb`. Move all `FeeRate` test to `types.rs`.
+- Add custom Harware Wallet Signer `HwiSigner` in `src/wallet/harwaresigner/` module.
## [v0.21.0] - [v0.20.0]
cc = { version = ">=1.0.64", optional = true }
socks = { version = "0.3", optional = true }
lazy_static = { version = "1.4", optional = true }
+hwi = { version = "0.2.2", optional = true }
bip39 = { version = "1.0.1", optional = true }
bitcoinconsensus = { version = "0.19.0-3", optional = true }
all-keys = ["keys-bip39"]
keys-bip39 = ["bip39"]
rpc = ["bitcoincore-rpc"]
+hardware-signer = ["hwi"]
# We currently provide mulitple implementations of `Blockchain`, all are
# blocking except for the `EsploraBlockchain` which can be either async or
test-rpc-legacy = ["rpc", "electrsd/electrs_0_8_10", "electrsd/bitcoind_0_20_0", "test-blockchains"]
test-esplora = ["electrsd/legacy", "electrsd/esplora_a33e97e1", "electrsd/bitcoind_22_0", "test-blockchains"]
test-md-docs = ["electrum"]
+test-hardware-signer = ["hardware-signer"]
[dev-dependencies]
lazy_static = "1.4"
--- /dev/null
+// Bitcoin Dev Kit
+// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
+//
+// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+//! HWI Signer
+//!
+//! This module contains a simple implementation of a Custom signer for rust-hwi
+
+use bitcoin::psbt::PartiallySignedTransaction;
+use bitcoin::secp256k1::{All, Secp256k1};
+use bitcoin::util::bip32::Fingerprint;
+
+use hwi::error::Error;
+use hwi::types::{HWIChain, HWIDevice};
+use hwi::HWIClient;
+
+use crate::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 a 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)
+ }
+}
+
+/// This implementation ignores `sign_options`
+impl TransactionSigner for HWISigner {
+ fn sign_transaction(
+ &self,
+ psbt: &mut PartiallySignedTransaction,
+ _sign_options: &crate::SignOptions,
+ _secp: &crate::wallet::utils::SecpCtx,
+ ) -> Result<(), SignerError> {
+ psbt.combine(self.client.sign_tx(psbt)?.psbt)
+ .expect("Failed to combine HW signed psbt with passed PSBT");
+ Ok(())
+ }
+}
#[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
pub mod verify;
+#[cfg(feature = "hardware-signer")]
+pub mod hardwaresigner;
+
pub use utils::IsDust;
#[allow(deprecated)]
// ...and checking that everything is fine
assert_fee_rate!(psbt, details.fee.unwrap_or(0), fee_rate);
}
+
+ #[cfg(feature = "test-hardware-signer")]
+ #[test]
+ fn test_create_signer() {
+ use crate::wallet::hardwaresigner::HWISigner;
+ use hwi::types::HWIChain;
+ use hwi::HWIClient;
+
+ let devices = HWIClient::enumerate().unwrap();
+ let device = devices.first().expect("No devices found");
+ let client = HWIClient::get_client(device, true, HWIChain::Regtest).unwrap();
+ let descriptors = client.get_descriptors(None).unwrap();
+ let custom_signer = HWISigner::from_device(device, HWIChain::Regtest).unwrap();
+
+ let (mut wallet, _, _) = get_funded_wallet(&descriptors.internal[0]);
+ wallet.add_signer(
+ KeychainKind::External,
+ SignerOrdering(200),
+ Arc::new(custom_signer),
+ );
+
+ let addr = wallet.get_address(LastUnused).unwrap();
+ let mut builder = wallet.build_tx();
+ builder.drain_to(addr.script_pubkey()).drain_wallet();
+ let (mut psbt, _) = builder.finish().unwrap();
+
+ let finalized = wallet.sign(&mut psbt, Default::default()).unwrap();
+ assert!(finalized);
+ }
}
InvalidSighash,
/// Error while computing the hash to sign
SighashError(sighash::Error),
+ /// Error while signing using hardware wallets
+ #[cfg(feature = "hardware-signer")]
+ HWIError(hwi::error::Error),
+}
+
+#[cfg(feature = "hardware-signer")]
+impl From<hwi::error::Error> for SignerError {
+ fn from(e: hwi::error::Error) -> Self {
+ SignerError::HWIError(e)
+ }
}
impl From<sighash::Error> for SignerError {