Add a custom signer for hardware wallets

Also add function to get funded wallet with coinbase
This commit is contained in:
wszdexdrf 2022-07-26 00:07:04 +05:30
parent 12507c707f
commit 76034772cb
No known key found for this signature in database
GPG Key ID: 4FAE8EF51A1296E5
5 changed files with 110 additions and 0 deletions

View File

@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [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]

View File

@ -33,6 +33,7 @@ rocksdb = { version = "0.14", default-features = false, features = ["snappy"], o
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 }
@ -61,6 +62,7 @@ key-value-db = ["sled"]
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
@ -93,6 +95,7 @@ test-rpc = ["rpc", "electrsd/electrs_0_8_10", "electrsd/bitcoind_22_0", "test-bl
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"

View File

@ -0,0 +1,64 @@
// 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(())
}
}

View File

@ -48,6 +48,9 @@ pub(crate) mod utils;
#[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
pub mod verify;
#[cfg(feature = "hardware-signer")]
pub mod hardwaresigner;
pub use utils::IsDust;
#[allow(deprecated)]
@ -5414,4 +5417,33 @@ pub(crate) mod test {
// ...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);
}
}

View File

@ -159,6 +159,16 @@ pub enum SignerError {
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 {