Add a custom signer for hardware wallets
Also add function to get funded wallet with coinbase
This commit is contained in:
parent
12507c707f
commit
76034772cb
@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
- Add capacity to create FeeRate from sats/kvbytes and sats/kwu.
|
- 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`.
|
- 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]
|
## [v0.21.0] - [v0.20.0]
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ rocksdb = { version = "0.14", default-features = false, features = ["snappy"], o
|
|||||||
cc = { version = ">=1.0.64", optional = true }
|
cc = { version = ">=1.0.64", optional = true }
|
||||||
socks = { version = "0.3", optional = true }
|
socks = { version = "0.3", optional = true }
|
||||||
lazy_static = { version = "1.4", optional = true }
|
lazy_static = { version = "1.4", optional = true }
|
||||||
|
hwi = { version = "0.2.2", optional = true }
|
||||||
|
|
||||||
bip39 = { version = "1.0.1", optional = true }
|
bip39 = { version = "1.0.1", optional = true }
|
||||||
bitcoinconsensus = { version = "0.19.0-3", optional = true }
|
bitcoinconsensus = { version = "0.19.0-3", optional = true }
|
||||||
@ -61,6 +62,7 @@ key-value-db = ["sled"]
|
|||||||
all-keys = ["keys-bip39"]
|
all-keys = ["keys-bip39"]
|
||||||
keys-bip39 = ["bip39"]
|
keys-bip39 = ["bip39"]
|
||||||
rpc = ["bitcoincore-rpc"]
|
rpc = ["bitcoincore-rpc"]
|
||||||
|
hardware-signer = ["hwi"]
|
||||||
|
|
||||||
# We currently provide mulitple implementations of `Blockchain`, all are
|
# We currently provide mulitple implementations of `Blockchain`, all are
|
||||||
# blocking except for the `EsploraBlockchain` which can be either async or
|
# 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-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-esplora = ["electrsd/legacy", "electrsd/esplora_a33e97e1", "electrsd/bitcoind_22_0", "test-blockchains"]
|
||||||
test-md-docs = ["electrum"]
|
test-md-docs = ["electrum"]
|
||||||
|
test-hardware-signer = ["hardware-signer"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
|
64
src/wallet/hardwaresigner.rs
Normal file
64
src/wallet/hardwaresigner.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
@ -48,6 +48,9 @@ pub(crate) mod utils;
|
|||||||
#[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
|
||||||
pub mod verify;
|
pub mod verify;
|
||||||
|
|
||||||
|
#[cfg(feature = "hardware-signer")]
|
||||||
|
pub mod hardwaresigner;
|
||||||
|
|
||||||
pub use utils::IsDust;
|
pub use utils::IsDust;
|
||||||
|
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
@ -5414,4 +5417,33 @@ pub(crate) mod test {
|
|||||||
// ...and checking that everything is fine
|
// ...and checking that everything is fine
|
||||||
assert_fee_rate!(psbt, details.fee.unwrap_or(0), fee_rate);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,6 +159,16 @@ pub enum SignerError {
|
|||||||
InvalidSighash,
|
InvalidSighash,
|
||||||
/// Error while computing the hash to sign
|
/// Error while computing the hash to sign
|
||||||
SighashError(sighash::Error),
|
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 {
|
impl From<sighash::Error> for SignerError {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user