Skip to content

Commit

Permalink
Merge branch 'master' into separate-python
Browse files Browse the repository at this point in the history
  • Loading branch information
ben-kaufman authored Sep 16, 2024
2 parents fe0f111 + ecba450 commit 9afb70d
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 8 deletions.
9 changes: 5 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ jobs:
matrix:
rust:
- version: stable # STABLE
features: miniscript
- version: 1.63.0 # MSRV
features: miniscript
features:
- miniscript
- signer
emulator:
- name: trezor
- name: ledger
Expand Down Expand Up @@ -79,12 +80,12 @@ jobs:
- name: Download and Install HWI
run: ./get_hwi.sh
- name: Test python
run: cargo test --features ${{ matrix.rust.features }} test_python -- --test-threads=1
run: cargo test --features ${{ matrix.features }} test_python -- --test-threads=1
- name: Restart simulator
if: matrix.emulator.name == 'ledger'
run: docker restart simulator
- name: Test binary
run: cargo test --features ${{ matrix.rust.features }} test_binary -- --test-threads=1
run: cargo test --features ${{ matrix.features }} test_binary -- --test-threads=1
- name: Wipe python
run: cargo test test_wipe_device_pyhton -- --ignored --test-threads=1
- name: Restart simulator
Expand Down
7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "hwi"
version = "0.9.0"
version = "0.10.0"
authors = ["Daniela Brozzoni <[email protected]>"]
edition = "2018"
license = "MIT"
Expand All @@ -12,14 +12,17 @@ readme = "README.md"

[dependencies]
bitcoin = { version = "0.32", features = ["serde", "base64"] }
pyo3 = { version = "0.21.2", features = ["auto-initialize"] }
serde = { version = "^1.0", features = ["derive"] }
serde_json = { version = "^1.0" }
pyo3 = { version = "0.21.2", features = ["auto-initialize"] }

bdk_wallet = { version = "1.0.0-beta.3", optional = true }
miniscript = { version = "12.0", features = ["serde"], optional = true }

[dev-dependencies]
serial_test = "0.6.0"

[features]
doctest = []
signer = ["dep:bdk_wallet"]
miniscript = ["dep:miniscript"]
51 changes: 49 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! Rust wrapper for the [Bitcoin Hardware Wallet Interface](https://github.com/bitcoin-core/HWI/).
//! This crate is contains both:
//! - [`HWIClient`]: A Rust wrapper for the [Bitcoin Hardware Wallet Interface](https://github.com/bitcoin-core/HWI/).
//! - [`HWISigner`]: An implementation of a [`TransactionSigner`] to be used with hardware wallets, that relies on [`HWIClient`].
//!
//! # Example - display address with path
//! # HWIClient Example:
//! ## Display address with path
//! ```no_run
//! use bitcoin::bip32::{ChildNumber, DerivationPath};
//! use hwi::error::Error;
Expand Down Expand Up @@ -30,19 +33,63 @@
//! Ok(())
//! }
//! ```
//!
//! # HWISigner Example:
//! ## Add custom [`HWISigner`] to [`Wallet`]
//! ```no_run
//! # #[cfg(feature = "signer")]
//! # {
//! use bdk_wallet::bitcoin::Network;
//! use bdk_wallet::descriptor::Descriptor;
//! use bdk_wallet::signer::SignerOrdering;
//! use bdk_wallet::{KeychainKind, SignOptions, Wallet};
//! use hwi::{HWIClient, HWISigner};
//! use std::str::FromStr;
//! use std::sync::Arc;
//!
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let mut devices = HWIClient::enumerate()?;
//! if devices.is_empty() {
//! panic!("No devices found!");
//! }
//! let first_device = devices.remove(0)?;
//! let custom_signer = HWISigner::from_device(&first_device, Network::Testnet.into())?;
//!
//! let mut wallet = Wallet::create("", "")
//! .network(Network::Testnet)
//! .create_wallet_no_persist()?;
//!
//! // Adding the hardware signer to the BDK wallet
//! wallet.add_signer(
//! KeychainKind::External,
//! SignerOrdering(200),
//! Arc::new(custom_signer),
//! );
//!
//! Ok(())
//! }
//! # }
//! ```
//!
//! [`TransactionSigner`]: https://docs.rs/bdk_wallet/latest/bdk_wallet/signer/trait.TransactionSigner.html
//! [`Wallet`]: https://docs.rs/bdk_wallet/1.0.0-beta.1/bdk_wallet/struct.Wallet.html
#[cfg(test)]
#[macro_use]
extern crate serial_test;
extern crate core;

pub use interface::HWIClient;
#[cfg(feature = "signer")]
pub use signer::HWISigner;

#[cfg(feature = "doctest")]
pub mod doctest;
pub mod error;
pub mod implementations;
pub mod interface;
#[cfg(feature = "signer")]
pub mod signer;
pub mod types;

#[cfg(test)]
Expand Down
55 changes: 55 additions & 0 deletions src/signer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use bdk_wallet::bitcoin::bip32::Fingerprint;
use bdk_wallet::bitcoin::secp256k1::{All, Secp256k1};
use bdk_wallet::bitcoin::Psbt;

use crate::error::Error;
use crate::types::{HWIChain, HWIDevice};
use crate::HWIClient;

use bdk_wallet::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 an 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)
}
}

impl TransactionSigner for HWISigner {
fn sign_transaction(
&self,
psbt: &mut Psbt,
_sign_options: &bdk_wallet::SignOptions,
_secp: &Secp256k1<All>,
) -> Result<(), SignerError> {
psbt.combine(
self.client
.sign_tx(psbt)
.map_err(|e| {
SignerError::External(format!("While signing with hardware wallet: {}", e))
})?
.psbt,
)
.expect("Failed to combine HW signed psbt with passed PSBT");
Ok(())
}
}

0 comments on commit 9afb70d

Please sign in to comment.