Skip to content

Commit 2018b3e

Browse files
committed
Add a new Signer for BDK
Update MSRV
1 parent 8285f66 commit 2018b3e

File tree

5 files changed

+105
-1
lines changed

5 files changed

+105
-1
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
matrix:
2828
rust:
2929
- version: 1.61.0 # STABLE
30-
- version: 1.41.1 # MSRV
30+
- version: 1.56.1 # MSRV
3131
emulator:
3232
- name: trezor
3333
- name: ledger

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ bitcoin = { version = "0.28", features = ["use-serde", "base64"] }
1414
serde = { version = "^1.0", features = ["derive"] }
1515
serde_json = { version = "^1.0" }
1616
pyo3 = { version = "0.15.1", features = ["auto-initialize"]}
17+
bdk = "0.20.0"
1718

1819
[dev-dependencies]
1920
serial_test = "0.6.0"

src/interface.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ macro_rules! deserialize_obj {
2626
}
2727

2828
/// Convenience class containing required Python objects
29+
#[derive(Debug)]
2930
struct HWILib {
3031
commands: Py<PyModule>,
3132
json_dumps: Py<PyAny>,
@@ -44,6 +45,7 @@ impl HWILib {
4445
}
4546
}
4647

48+
#[derive(Debug)]
4749
pub struct HWIClient {
4850
hwilib: HWILib,
4951
hw_client: PyObject,

src/lib.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,27 @@ pub use interface::HWIClient;
3131

3232
pub mod error;
3333
pub mod interface;
34+
pub mod signer;
3435
pub mod types;
3536

3637
#[cfg(test)]
3738
mod tests {
39+
use crate::signer::HWISigner;
3840
use crate::types;
3941
use crate::HWIClient;
4042
use std::collections::BTreeMap;
4143
use std::str::FromStr;
44+
use std::sync::Arc;
4245

46+
use bdk::database::MemoryDatabase;
47+
use bdk::signer::SignerOrdering;
48+
use bdk::KeychainKind;
49+
use bdk::SignOptions;
50+
use bdk::Wallet;
4351
use bitcoin::psbt::{Input, Output};
4452
use bitcoin::util::bip32::{DerivationPath, KeySource};
53+
use bitcoin::Address;
54+
use bitcoin::Network;
4555
use bitcoin::{secp256k1, Transaction};
4656
use bitcoin::{TxIn, TxOut};
4757

@@ -320,4 +330,44 @@ mod tests {
320330
client.wipe_device().unwrap();
321331
}
322332
}
333+
#[test]
334+
#[serial]
335+
fn test_create_signer() {
336+
let descriptors = get_first_device().get_descriptors(None).unwrap();
337+
let devices = HWIClient::enumerate().unwrap();
338+
let custom_signer =
339+
HWISigner::from_device(devices.first().unwrap(), types::HWIChain::Test).unwrap();
340+
341+
let mut wallet = Wallet::new(
342+
&descriptors.internal[0],
343+
Some(&descriptors.receive[0]),
344+
Network::Testnet,
345+
MemoryDatabase::default(),
346+
)
347+
.unwrap();
348+
wallet.add_signer(
349+
KeychainKind::External,
350+
SignerOrdering(200),
351+
Arc::new(custom_signer),
352+
);
353+
354+
let client =
355+
bdk::electrum_client::Client::new("ssl://electrum.blockstream.info:60002").unwrap();
356+
let blockchain = bdk::blockchain::ElectrumBlockchain::from(client);
357+
wallet
358+
.sync(&blockchain, bdk::SyncOptions::default())
359+
.unwrap();
360+
361+
let balance = wallet.get_balance().unwrap();
362+
let faucet_address = Address::from_str("mv4rnyY3Su5gjcDNzbMLKBQkBicCtHUtFB").unwrap();
363+
364+
let mut tx_builder = wallet.build_tx();
365+
tx_builder
366+
.add_recipient(faucet_address.script_pubkey(), balance / 2)
367+
.enable_rbf();
368+
let (mut psbt, _tx_details) = tx_builder.finish().unwrap();
369+
let finalized = wallet.sign(&mut psbt, SignOptions::default()).unwrap();
370+
371+
assert!(finalized, "Tx has not been finalized");
372+
}
323373
}

src/signer.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use bdk::signer::{SignerCommon, SignerError, SignerId, TransactionSigner};
2+
use bitcoin::{
3+
psbt::PartiallySignedTransaction,
4+
secp256k1::{All, Secp256k1},
5+
util::bip32::Fingerprint,
6+
};
7+
8+
use crate::{
9+
error::Error,
10+
types::{HWIChain, HWIDevice},
11+
HWIClient,
12+
};
13+
14+
#[derive(Debug)]
15+
pub struct HWISigner {
16+
fingerprint: Fingerprint,
17+
client: HWIClient,
18+
}
19+
20+
impl HWISigner {
21+
pub fn from_device(device: &HWIDevice, chain: HWIChain) -> Result<HWISigner, Error> {
22+
let client = HWIClient::get_client(device, false, chain)?;
23+
Ok(HWISigner {
24+
fingerprint: device.fingerprint,
25+
client,
26+
})
27+
}
28+
}
29+
30+
impl SignerCommon for HWISigner {
31+
fn id(&self, _secp: &Secp256k1<All>) -> SignerId {
32+
SignerId::Fingerprint(self.fingerprint)
33+
}
34+
}
35+
36+
impl TransactionSigner for HWISigner {
37+
fn sign_transaction(
38+
&self,
39+
psbt: &mut PartiallySignedTransaction,
40+
_secp: &Secp256k1<All>,
41+
) -> Result<(), SignerError> {
42+
psbt.combine(
43+
self.client
44+
.sign_tx(psbt)
45+
.expect("Hardware Wallet couldn't sign transaction")
46+
.psbt,
47+
)
48+
.expect("Failed to combine HW signed psbt with passed PSBT");
49+
Ok(())
50+
}
51+
}

0 commit comments

Comments
 (0)