Skip to content

Commit 100ea8d

Browse files
committed
adding hardware signers
1 parent c00258b commit 100ea8d

File tree

5 files changed

+200
-3
lines changed

5 files changed

+200
-3
lines changed

Cargo.lock

Lines changed: 120 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ esplora-reqwest = ["esplora", "bdk/use-esplora-reqwest", "bdk/reqwest-default-tl
7171
# Use this to consensus verify transactions at sync time
7272
verify = ["bdk/verify"]
7373

74+
# Use hardware wallets to sign transactions
75+
hardware-signer = ["bdk/hardware-signer"]
76+
7477
# Extra utility tools
7578
# Compile policies
7679
compiler = ["bdk/compiler"]
@@ -93,4 +96,4 @@ regtest-electrum = ["regtest-node", "electrum", "electrsd/electrs_0_8_10"]
9396
# This feature is used to run `cargo check` in our CI targeting wasm. It's not recommended
9497
# for libraries to explicitly include the "getrandom/js" feature, so we only do it when
9598
# necessary for running our CI. See: https://docs.rs/getrandom/0.2.8/getrandom/#webassembly-support
96-
dev-getrandom-wasm = ["getrandom/js"]
99+
dev-getrandom-wasm = ["getrandom/js"]

src/commands.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,9 @@ pub enum KeySubCommand {
565565
#[clap(name = "PATH", short = 'p', long = "path")]
566566
path: DerivationPath,
567567
},
568+
#[cfg(feature = "hardware-signer")]
569+
/// List the public descriptors of the available hardware wallets
570+
Hardware {},
568571
}
569572

570573
/// Subcommands available in REPL mode.

src/handlers.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,20 @@ use bdk::descriptor::Segwitv0;
5050
use bdk::descriptor::{Descriptor, Legacy, Miniscript};
5151
#[cfg(all(feature = "reserves", feature = "electrum"))]
5252
use bdk::electrum_client::{Client, ElectrumApi};
53+
#[cfg(feature = "hardware-signer")]
54+
use bdk::hwi::{
55+
interface::HWIClient,
56+
types::{HWIChain, HWIDescriptor},
57+
};
5358
use bdk::keys::bip39::{Language, Mnemonic, WordCount};
5459
use bdk::keys::DescriptorKey::Secret;
5560
use bdk::keys::KeyError::{InvalidNetwork, Message};
5661
use bdk::keys::{DerivableKey, DescriptorKey, ExtendedKey, GeneratableKey, GeneratedKey};
5762
use bdk::miniscript::miniscript;
5863
#[cfg(feature = "compiler")]
5964
use bdk::miniscript::policy::Concrete;
65+
#[cfg(feature = "hardware-signer")]
66+
use bdk::wallet::signer::SignerError;
6067
use bdk::SignOptions;
6168
#[cfg(all(feature = "reserves", feature = "electrum"))]
6269
use bdk::{bitcoin::Address, blockchain::Capability};
@@ -487,6 +494,26 @@ pub(crate) fn handle_key_subcommand(
487494
Err(Error::Key(Message("Invalid key variant".to_string())))
488495
}
489496
}
497+
#[cfg(feature = "hardware-signer")]
498+
KeySubCommand::Hardware {} => {
499+
let chain = match network {
500+
Network::Bitcoin => HWIChain::Main,
501+
Network::Testnet => HWIChain::Test,
502+
Network::Regtest => HWIChain::Regtest,
503+
Network::Signet => HWIChain::Signet,
504+
};
505+
let devices = HWIClient::enumerate().map_err(|e| SignerError::from(e))?;
506+
let descriptors = devices.iter().map(|device_| {
507+
let device = device_.clone()
508+
.map_err(|e| SignerError::from(e))?;
509+
let client = HWIClient::get_client(&device, true, chain.clone())
510+
.map_err(|e| SignerError::from(e))?;
511+
let descriptors: HWIDescriptor<String> = client.get_descriptors(None)
512+
.map_err(|e| SignerError::from(e))?;
513+
Ok(json!({"device": device.model, "receiving": descriptors.receive[0].to_string(), "change": descriptors.internal[0]}))
514+
}).collect::<Result<Vec<_>, Error>>()?;
515+
Ok(json!(descriptors))
516+
}
490517
}
491518
}
492519

src/utils.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,18 @@ use bdk::database::any::SledDbConfiguration;
4343
#[cfg(feature = "sqlite-db")]
4444
use bdk::database::any::SqliteDbConfiguration;
4545
use bdk::database::{AnyDatabase, AnyDatabaseConfig, BatchDatabase, ConfigurableDatabase};
46+
#[cfg(feature = "hardware-signer")]
47+
use bdk::hwi::{interface::HWIClient, types::HWIChain};
48+
#[cfg(feature = "hardware-signer")]
49+
use bdk::wallet::hardwaresigner::HWISigner;
50+
#[cfg(feature = "hardware-signer")]
51+
use bdk::wallet::signer::{SignerError, SignerOrdering};
4652
use bdk::wallet::wallet_name_from_descriptor;
53+
#[cfg(feature = "hardware-signer")]
54+
use bdk::KeychainKind;
4755
use bdk::{Error, Wallet};
56+
#[cfg(feature = "hardware-signer")]
57+
use std::sync::Arc;
4858

4959
/// Create a randomized wallet name from the descriptor checksum.
5060
/// If wallet options already includes a name, use that instead.
@@ -497,5 +507,41 @@ where
497507
let descriptor = wallet_opts.descriptor.as_str();
498508
let change_descriptor = wallet_opts.change_descriptor.as_deref();
499509
let wallet = Wallet::new(descriptor, change_descriptor, network, database)?;
510+
511+
#[cfg(feature = "hardware-signer")]
512+
let wallet = add_hardware_signers(wallet, network)?;
513+
514+
Ok(wallet)
515+
}
516+
517+
/// Add hardware wallets as signers to the wallet
518+
#[cfg(feature = "hardware-signer")]
519+
fn add_hardware_signers<D>(wallet: Wallet<D>, network: Network) -> Result<Wallet<D>, Error>
520+
where
521+
D: BatchDatabase,
522+
{
523+
let mut wallet = wallet;
524+
let chain = match network {
525+
Network::Bitcoin => HWIChain::Main,
526+
Network::Testnet => HWIChain::Test,
527+
Network::Regtest => HWIChain::Regtest,
528+
Network::Signet => HWIChain::Signet,
529+
};
530+
let devices = HWIClient::enumerate().map_err(|e| SignerError::from(e))?;
531+
for device in devices {
532+
let device = device.map_err(|e| SignerError::from(e))?;
533+
// Creating a custom signer from the device
534+
let custom_signer =
535+
HWISigner::from_device(&device, chain.clone()).map_err(|e| SignerError::from(e))?;
536+
537+
// Adding the hardware signer to the BDK wallet
538+
wallet.add_signer(
539+
KeychainKind::External,
540+
SignerOrdering(200),
541+
Arc::new(custom_signer),
542+
);
543+
println!("Added {} as a signer to the wallet.", device.model);
544+
}
545+
500546
Ok(wallet)
501547
}

0 commit comments

Comments
 (0)