Skip to content

Commit

Permalink
adding hardware signers
Browse files Browse the repository at this point in the history
  • Loading branch information
ulrichard committed Jan 30, 2023
1 parent 5c2a97e commit 927b066
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ page. See [DEVELOPMENT_CYCLE.md](DEVELOPMENT_CYCLE.md) for more details.

## [Unreleased]

- Added hardware signers through the use of HWI

## [0.6.0]

- Add distinct `key-value-db` and `sqlite-db` features, keep default as `key-value-db`
Expand Down
122 changes: 120 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ esplora-reqwest = ["esplora", "bdk/use-esplora-reqwest", "bdk/reqwest-default-tl
# Use this to consensus verify transactions at sync time
verify = ["bdk/verify"]

# Use hardware wallets to sign transactions
hardware-signer = ["bdk/hardware-signer"]

# Extra utility tools
# Compile policies
compiler = ["bdk/compiler"]
Expand All @@ -91,4 +94,4 @@ regtest-electrum = ["regtest-node", "electrum", "electrsd/electrs_0_8_10"]
# This feature is used to run `cargo check` in our CI targeting wasm. It's not recommended
# for libraries to explicitly include the "getrandom/js" feature, so we only do it when
# necessary for running our CI. See: https://docs.rs/getrandom/0.2.8/getrandom/#webassembly-support
dev-getrandom-wasm = ["getrandom/js"]
dev-getrandom-wasm = ["getrandom/js"]
3 changes: 3 additions & 0 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,9 @@ pub enum KeySubCommand {
#[clap(name = "PATH", short = 'p', long = "path")]
path: DerivationPath,
},
#[cfg(feature = "hardware-signer")]
/// List the public descriptors of the available hardware wallets
Hardware {},
}

/// Subcommands available in REPL mode.
Expand Down
27 changes: 27 additions & 0 deletions src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,20 @@ use bdk::descriptor::Segwitv0;
use bdk::descriptor::{Descriptor, Legacy, Miniscript};
#[cfg(all(feature = "reserves", feature = "electrum"))]
use bdk::electrum_client::{Client, ElectrumApi};
#[cfg(feature = "hardware-signer")]
use bdk::hwi::{
interface::HWIClient,
types::{HWIChain, HWIDescriptor},
};
use bdk::keys::bip39::{Language, Mnemonic, WordCount};
use bdk::keys::DescriptorKey::Secret;
use bdk::keys::KeyError::{InvalidNetwork, Message};
use bdk::keys::{DerivableKey, DescriptorKey, ExtendedKey, GeneratableKey, GeneratedKey};
use bdk::miniscript::miniscript;
#[cfg(feature = "compiler")]
use bdk::miniscript::policy::Concrete;
#[cfg(feature = "hardware-signer")]
use bdk::wallet::signer::SignerError;
use bdk::SignOptions;
#[cfg(all(feature = "reserves", feature = "electrum"))]
use bdk::{bitcoin::Address, blockchain::Capability};
Expand Down Expand Up @@ -487,6 +494,26 @@ pub(crate) fn handle_key_subcommand(
Err(Error::Key(Message("Invalid key variant".to_string())))
}
}
#[cfg(feature = "hardware-signer")]
KeySubCommand::Hardware {} => {
let chain = match network {
Network::Bitcoin => HWIChain::Main,
Network::Testnet => HWIChain::Test,
Network::Regtest => HWIChain::Regtest,
Network::Signet => HWIChain::Signet,
};
let devices = HWIClient::enumerate().map_err(|e| SignerError::from(e))?;
let descriptors = devices.iter().map(|device_| {
let device = device_.clone()
.map_err(|e| SignerError::from(e))?;
let client = HWIClient::get_client(&device, true, chain.clone())
.map_err(|e| SignerError::from(e))?;
let descriptors: HWIDescriptor<String> = client.get_descriptors(None)
.map_err(|e| SignerError::from(e))?;
Ok(json!({"device": device.model, "receiving": descriptors.receive[0].to_string(), "change": descriptors.internal[0]}))
}).collect::<Result<Vec<_>, Error>>()?;
Ok(json!(descriptors))
}
}
}

Expand Down
46 changes: 46 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,18 @@ use bdk::database::any::SledDbConfiguration;
#[cfg(feature = "sqlite-db")]
use bdk::database::any::SqliteDbConfiguration;
use bdk::database::{AnyDatabase, AnyDatabaseConfig, BatchDatabase, ConfigurableDatabase};
#[cfg(feature = "hardware-signer")]
use bdk::hwi::{interface::HWIClient, types::HWIChain};
#[cfg(feature = "hardware-signer")]
use bdk::wallet::hardwaresigner::HWISigner;
#[cfg(feature = "hardware-signer")]
use bdk::wallet::signer::{SignerError, SignerOrdering};
use bdk::wallet::wallet_name_from_descriptor;
#[cfg(feature = "hardware-signer")]
use bdk::KeychainKind;
use bdk::{Error, Wallet};
#[cfg(feature = "hardware-signer")]
use std::sync::Arc;

/// Create a randomized wallet name from the descriptor checksum.
/// If wallet options already includes a name, use that instead.
Expand Down Expand Up @@ -497,5 +507,41 @@ where
let descriptor = wallet_opts.descriptor.as_str();
let change_descriptor = wallet_opts.change_descriptor.as_deref();
let wallet = Wallet::new(descriptor, change_descriptor, network, database)?;

#[cfg(feature = "hardware-signer")]
let wallet = add_hardware_signers(wallet, network)?;

Ok(wallet)
}

/// Add hardware wallets as signers to the wallet
#[cfg(feature = "hardware-signer")]
fn add_hardware_signers<D>(wallet: Wallet<D>, network: Network) -> Result<Wallet<D>, Error>
where
D: BatchDatabase,
{
let mut wallet = wallet;
let chain = match network {
Network::Bitcoin => HWIChain::Main,
Network::Testnet => HWIChain::Test,
Network::Regtest => HWIChain::Regtest,
Network::Signet => HWIChain::Signet,
};
let devices = HWIClient::enumerate().map_err(|e| SignerError::from(e))?;
for device in devices {
let device = device.map_err(|e| SignerError::from(e))?;
// Creating a custom signer from the device
let custom_signer =
HWISigner::from_device(&device, chain.clone()).map_err(|e| SignerError::from(e))?;

// Adding the hardware signer to the BDK wallet
wallet.add_signer(
KeychainKind::External,
SignerOrdering(200),
Arc::new(custom_signer),
);
println!("Added {} as a signer to the wallet.", device.model);
}

Ok(wallet)
}

0 comments on commit 927b066

Please sign in to comment.