Merge bitcoindevkit/bdk#758: Add HWI example in docs
1437e1ecfe663b819156d98c5e1975fb357a763f Add the hardware_signer example (Daniela Brozzoni) 1a71eb1f4736651ad82e0abd64792b6cc7b16c20 Update the hardwaresigner module documentation (Daniela Brozzoni) 0695e9fb3e41727e5732561a993411147487afd3 Bump HWI to 0.2.3 (Daniela Brozzoni) a4a43ea86060fa0a62b47dedc7de820459b3a472 Re-export HWI if the hardware-signer feature is set (Daniela Brozzoni) Pull request description: ### Description ### Notes to the reviewers ### Changelog notice - bdk re-exports the `hwi` create when the feature `hardware-signer` is on - Add `examples/hardware_signer.rs` ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing ACKs for top commit: afilini: ACK 1437e1ecfe663b819156d98c5e1975fb357a763f Tree-SHA512: 181f4d14dce11e19497fbf30e0af8de21c2c210d37129d7d879ed5670ed09a25be1c8d371389c431e18df9e76870cf5e4afe7b29a6c05fe59b3e1816bc8cf673
This commit is contained in:
commit
ea47d7a35b
@ -31,7 +31,7 @@ async-trait = { version = "0.1", optional = true }
|
||||
rocksdb = { version = "0.14", default-features = false, features = ["snappy"], optional = true }
|
||||
cc = { version = ">=1.0.64", optional = true }
|
||||
socks = { version = "0.3", optional = true }
|
||||
hwi = { version = "0.2.2", optional = true }
|
||||
hwi = { version = "0.2.3", optional = true }
|
||||
|
||||
bip39 = { version = "1.0.1", optional = true }
|
||||
bitcoinconsensus = { version = "0.19.0-3", optional = true }
|
||||
@ -128,6 +128,11 @@ name = "psbt_signer"
|
||||
path = "examples/psbt_signer.rs"
|
||||
required-features = ["electrum"]
|
||||
|
||||
[[example]]
|
||||
name = "hardware_signer"
|
||||
path = "examples/hardware_signer.rs"
|
||||
required-features = ["electrum", "hardware-signer"]
|
||||
|
||||
[workspace]
|
||||
members = ["macros"]
|
||||
[package.metadata.docs.rs]
|
||||
|
103
examples/hardware_signer.rs
Normal file
103
examples/hardware_signer.rs
Normal file
@ -0,0 +1,103 @@
|
||||
use bdk::bitcoin::{Address, Network};
|
||||
use bdk::blockchain::{Blockchain, ElectrumBlockchain};
|
||||
use bdk::database::MemoryDatabase;
|
||||
use bdk::hwi::{types::HWIChain, HWIClient};
|
||||
use bdk::signer::SignerOrdering;
|
||||
use bdk::wallet::{hardwaresigner::HWISigner, AddressIndex};
|
||||
use bdk::{FeeRate, KeychainKind, SignOptions, SyncOptions, Wallet};
|
||||
use electrum_client::Client;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
// This example shows how to sync a wallet, create a transaction, sign it
|
||||
// and broadcast it using an external hardware wallet.
|
||||
// The hardware wallet must be connected to the computer and unlocked before
|
||||
// running the example. Also, the `hwi` python package should be installed
|
||||
// and available in the environment.
|
||||
//
|
||||
// To avoid loss of funds, consider using an hardware wallet simulator:
|
||||
// * Coldcard: https://github.com/Coldcard/firmware
|
||||
// * Ledger: https://github.com/LedgerHQ/speculos
|
||||
// * Trezor: https://docs.trezor.io/trezor-firmware/core/emulator/index.html
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Hold tight, I'm connecting to your hardware wallet...");
|
||||
|
||||
// Listing all the available hardware wallet devices...
|
||||
let devices = HWIClient::enumerate()?;
|
||||
let first_device = devices
|
||||
.first()
|
||||
.expect("No devices found. Either plug in a hardware wallet, or start a simulator.");
|
||||
// ...and creating a client out of the first one
|
||||
let client = HWIClient::get_client(first_device, true, HWIChain::Test)?;
|
||||
println!("Look what I found, a {}!", first_device.model);
|
||||
|
||||
// Getting the HW's public descriptors
|
||||
let descriptors = client.get_descriptors(None)?;
|
||||
println!(
|
||||
"The hardware wallet's descriptor is: {}",
|
||||
descriptors.receive[0]
|
||||
);
|
||||
|
||||
// Creating a custom signer from the device
|
||||
let custom_signer = HWISigner::from_device(first_device, HWIChain::Test)?;
|
||||
let mut wallet = Wallet::new(
|
||||
&descriptors.receive[0],
|
||||
Some(&descriptors.internal[0]),
|
||||
Network::Testnet,
|
||||
MemoryDatabase::default(),
|
||||
)?;
|
||||
|
||||
// Adding the hardware signer to the BDK wallet
|
||||
wallet.add_signer(
|
||||
KeychainKind::External,
|
||||
SignerOrdering(200),
|
||||
Arc::new(custom_signer),
|
||||
);
|
||||
|
||||
// create client for Blockstream's testnet electrum server
|
||||
let blockchain =
|
||||
ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?);
|
||||
|
||||
println!("Syncing the wallet...");
|
||||
wallet.sync(&blockchain, SyncOptions::default())?;
|
||||
|
||||
// get deposit address
|
||||
let deposit_address = wallet.get_address(AddressIndex::New)?;
|
||||
|
||||
let balance = wallet.get_balance()?;
|
||||
println!("Wallet balances in SATs: {}", balance);
|
||||
|
||||
if balance.get_total() < 10000 {
|
||||
println!(
|
||||
"Send some sats from the u01.net testnet faucet to address '{addr}'.\nFaucet URL: https://bitcoinfaucet.uo1.net/?to={addr}",
|
||||
addr = deposit_address.address
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let return_address = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")?;
|
||||
let (mut psbt, _details) = {
|
||||
let mut builder = wallet.build_tx();
|
||||
builder
|
||||
.drain_wallet()
|
||||
.drain_to(return_address.script_pubkey())
|
||||
.enable_rbf()
|
||||
.fee_rate(FeeRate::from_sat_per_vb(5.0));
|
||||
builder.finish()?
|
||||
};
|
||||
|
||||
// `sign` will call the hardware wallet asking for a signature
|
||||
assert!(
|
||||
wallet.sign(&mut psbt, SignOptions::default())?,
|
||||
"The hardware wallet couldn't finalize the transaction :("
|
||||
);
|
||||
|
||||
println!("Let's broadcast your tx...");
|
||||
let raw_transaction = psbt.extract_tx();
|
||||
let txid = raw_transaction.txid();
|
||||
|
||||
blockchain.broadcast(&raw_transaction)?;
|
||||
println!("Transaction broadcasted! TXID: {txid}.\nExplorer URL: https://mempool.space/testnet/tx/{txid}", txid = txid);
|
||||
|
||||
Ok(())
|
||||
}
|
@ -203,6 +203,8 @@ pub extern crate miniscript;
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_json;
|
||||
#[cfg(feature = "hardware-signer")]
|
||||
pub extern crate hwi;
|
||||
|
||||
#[cfg(all(feature = "reqwest", feature = "ureq"))]
|
||||
compile_error!("Features reqwest and ureq are mutually exclusive and cannot be enabled together");
|
||||
|
@ -11,7 +11,40 @@
|
||||
|
||||
//! HWI Signer
|
||||
//!
|
||||
//! This module contains a simple implementation of a Custom signer for rust-hwi
|
||||
//! This module contains HWISigner, an implementation of a [TransactionSigner] to be
|
||||
//! used with hardware wallets.
|
||||
//! ```no_run
|
||||
//! # use bdk::bitcoin::Network;
|
||||
//! # use bdk::database::MemoryDatabase;
|
||||
//! # use bdk::signer::SignerOrdering;
|
||||
//! # use bdk::wallet::hardwaresigner::HWISigner;
|
||||
//! # use bdk::wallet::AddressIndex::New;
|
||||
//! # use bdk::{FeeRate, KeychainKind, SignOptions, SyncOptions, Wallet};
|
||||
//! # use hwi::{types::HWIChain, HWIClient};
|
||||
//! # use std::sync::Arc;
|
||||
//! #
|
||||
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
//! let devices = HWIClient::enumerate()?;
|
||||
//! let first_device = devices.first().expect("No devices found!");
|
||||
//! let custom_signer = HWISigner::from_device(first_device, HWIChain::Test)?;
|
||||
//!
|
||||
//! # let mut wallet = Wallet::new(
|
||||
//! # "",
|
||||
//! # None,
|
||||
//! # Network::Testnet,
|
||||
//! # MemoryDatabase::default(),
|
||||
//! # )?;
|
||||
//! #
|
||||
//! // Adding the hardware signer to the BDK wallet
|
||||
//! wallet.add_signer(
|
||||
//! KeychainKind::External,
|
||||
//! SignerOrdering(200),
|
||||
//! Arc::new(custom_signer),
|
||||
//! );
|
||||
//!
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
use bitcoin::psbt::PartiallySignedTransaction;
|
||||
use bitcoin::secp256k1::{All, Secp256k1};
|
||||
|
Loading…
x
Reference in New Issue
Block a user