Skip to content

Commit 0c6eb33

Browse files
committed
feat(hwi): Update hwi lib to v0.0.30
1 parent 4a71d1d commit 0c6eb33

File tree

4 files changed

+120
-117
lines changed

4 files changed

+120
-117
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ tracing = "0.1.41"
2525
tracing-subscriber = "0.3.20"
2626
toml = "0.8.23"
2727
serde= {version = "1.0", features = ["derive"]}
28+
crossbeam ={ version = "0.8.4"}
2829

2930
# Optional dependencies
3031
bdk_bitcoind_rpc = { version = "0.21.0", features = ["std"], optional = true }
@@ -36,7 +37,7 @@ shlex = { version = "1.3.0", optional = true }
3637
payjoin = { version = "1.0.0-rc.1", features = ["v1", "v2", "io", "_test-utils"], optional = true}
3738
reqwest = { version = "0.12.23", default-features = false, optional = true }
3839
url = { version = "2.5.4", optional = true }
39-
async-hwi ={ version = "0.0.29", optional = true }
40+
async-hwi ={ version = "0.0.30", features = ["service"], optional = true }
4041

4142
[features]
4243
default = ["repl", "sqlite"]

src/handlers.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,7 +1204,7 @@ pub async fn handle_hwi_subcommand(
12041204
) -> Result<serde_json::Value, Error> {
12051205
match subcommand {
12061206
HwiSubCommand::Devices => {
1207-
let devices = crate::utils::connect_to_hardware_wallet(network, hwi_opts).await?;
1207+
let devices = crate::utils::get_hwi_device(network, hwi_opts).await?;
12081208
let device = if let Some(device) = devices {
12091209
json!({
12101210
"fingerprint": device.get_master_fingerprint().await?.to_string(),
@@ -1223,7 +1223,7 @@ pub async fn handle_hwi_subcommand(
12231223
Error::Generic("Wallet name is required for wallet registration".to_string())
12241224
})?;
12251225

1226-
let device = crate::utils::connect_to_hardware_wallet(network, hwi_opts).await?;
1226+
let device = crate::utils::get_hwi_device(network, hwi_opts).await?;
12271227

12281228
match device {
12291229
None => Ok(json!({
@@ -1263,7 +1263,7 @@ pub async fn handle_hwi_subcommand(
12631263
let wallet_opts = WalletOpts {
12641264
wallet: Some(wallet_name),
12651265
verbose: false,
1266-
ext_descriptor: ext_descriptor,
1266+
ext_descriptor,
12671267
int_descriptor: None,
12681268
#[cfg(any(
12691269
feature = "electrum",
@@ -1320,7 +1320,7 @@ pub async fn handle_hwi_subcommand(
13201320
HwiSubCommand::Sign { psbt } => {
13211321
let mut psbt = Psbt::from_str(&psbt)
13221322
.map_err(|e| Error::Generic(format!("Failed to parse PSBT: {e}")))?;
1323-
let device = crate::utils::connect_to_hardware_wallet(network, hwi_opts).await?;
1323+
let device = crate::utils::get_hwi_device(network, hwi_opts).await?;
13241324
let signed_psbt = if let Some(device) = device {
13251325
device
13261326
.sign_tx(&mut psbt)
@@ -1569,7 +1569,7 @@ pub(crate) async fn handle_command(cli_opts: CliOpts) -> Result<String, Error> {
15691569
subcommand,
15701570
} => {
15711571
let result = handle_hwi_subcommand(network, &hwi_opts, subcommand).await?;
1572-
Ok(serde_json::to_string_pretty(&result).map_err(|e| Error::SerdeJson(e))?)
1572+
Ok(serde_json::to_string_pretty(&result).map_err(Error::SerdeJson)?)
15731573
}
15741574
};
15751575
result

src/utils.rs

Lines changed: 49 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,7 @@ use bdk_wallet::{
3737
};
3838
use cli_table::{Cell, CellStruct, Style, Table};
3939
#[cfg(feature = "hwi")]
40-
use {
41-
crate::commands::HwiOpts,
42-
async_hwi::jade::{self, Jade},
43-
async_hwi::ledger::{HidApi, Ledger, LedgerSimulator, TransportHID},
44-
async_hwi::specter::{Specter, SpecterSimulator},
45-
async_hwi::{HWI, coldcard},
46-
async_hwi::{
47-
bitbox::api::runtime,
48-
bitbox::{BitBox02, PairingBitbox02WithLocalCache},
49-
},
50-
};
40+
use std::marker::Sync as MarkerSync;
5141

5242
#[cfg(any(
5343
feature = "electrum",
@@ -661,109 +651,61 @@ pub fn load_wallet_config(
661651
}
662652

663653
#[cfg(feature = "hwi")]
664-
pub async fn connect_to_hardware_wallet(
654+
pub async fn get_hwi_device(
665655
network: Network,
666-
hwi_opts: &HwiOpts,
667-
) -> Result<Option<Box<dyn HWI + Send>>, Error> {
668-
if let Ok(device) = SpecterSimulator::try_connect().await {
669-
return Ok(Some(device.into()));
670-
}
671-
672-
if let Ok(devices) = Specter::enumerate().await {
673-
if let Some(device) = devices.into_iter().next() {
674-
return Ok(Some(device.into()));
675-
}
676-
}
677-
678-
match Jade::enumerate().await {
679-
Err(e) => {
680-
println!("Jade enumeration error: {e:?}");
681-
}
682-
Ok(devices) => {
683-
for device in devices {
684-
let device = device.with_network(network);
685-
if let Ok(info) = device.get_info().await {
686-
if info.jade_state == jade::api::JadeState::Locked {
687-
if let Err(e) = device.auth().await {
688-
eprintln!("Jade auth error: {:?}", e);
689-
continue;
690-
}
656+
hwi_opts: &crate::commands::HwiOpts,
657+
) -> Result<Option<Arc<dyn async_hwi::HWI + Send + MarkerSync>>, Error> {
658+
use async_hwi::ledger::{HidApi, Ledger, TransportHID};
659+
use async_hwi::service::{HwiService, SigningDevice, SigningDeviceMsg};
660+
use crossbeam::channel;
661+
use std::time::{Duration, Instant};
662+
663+
let (sender, receiver) = channel::unbounded::<SigningDeviceMsg>();
664+
let service = HwiService::new(network, Some(tokio::runtime::Handle::current()));
665+
service.start(sender);
666+
667+
let timeout = Duration::from_secs(5);
668+
let start = Instant::now();
669+
670+
while start.elapsed() < timeout {
671+
if let Ok(SigningDeviceMsg::Update) = receiver.recv_timeout(Duration::from_millis(200)) {
672+
let devices = service.list();
673+
for (_, signing_device) in devices {
674+
if let SigningDevice::Supported(supported) = signing_device {
675+
let device = supported.device().clone();
676+
677+
if device.device_kind() == async_hwi::DeviceKind::Ledger {
678+
let wallet_name = hwi_opts.wallet.clone().ok_or_else(|| {
679+
Error::Generic("Wallet name required for Ledger".to_string())
680+
})?;
681+
let policy = hwi_opts.ext_descriptor.clone().ok_or_else(|| {
682+
Error::Generic("Descriptor required for Ledger policy".to_string())
683+
})?;
684+
685+
let api = HidApi::new().map_err(|e| Error::Generic(e.to_string()))?;
686+
let detected = Ledger::<TransportHID>::enumerate(&api)
687+
.next()
688+
.ok_or_else(|| Error::Generic("Ledger lost".to_string()))?;
689+
690+
let mut ledger = Ledger::<TransportHID>::connect(&api, detected)
691+
.map_err(|e| Error::Generic(e.to_string()))?;
692+
693+
ledger = ledger
694+
.with_wallet(wallet_name, &policy, None)
695+
.map_err(|e| Error::Generic(e.to_string()))?;
696+
697+
service.stop();
698+
return Ok(Some(Arc::new(ledger)));
691699
}
692-
return Ok(Some(device.into()));
693-
}
694-
}
695-
}
696-
}
697-
698-
if let Ok(device) = LedgerSimulator::try_connect().await {
699-
return Ok(Some(device.into()));
700-
}
701700

702-
let api = Box::new(HidApi::new().map_err(|e| Error::Generic(e.to_string()))?);
703-
704-
for device_info in api.device_list() {
705-
let ext_descriptor = hwi_opts.ext_descriptor.clone().ok_or_else(|| {
706-
Error::Generic("External descriptor is required for connecting to bitbox and coldcard hardware devices".to_string())
707-
})?;
708-
let wallet_name = hwi_opts.wallet.clone().ok_or_else(|| {
709-
Error::Generic(
710-
"Wallet name is required for connecting to bitbox and coldcard hardware devices"
711-
.to_string(),
712-
)
713-
})?;
714-
if async_hwi::bitbox::is_bitbox02(device_info) {
715-
if let Ok(device) = device_info
716-
.open_device(&api)
717-
.map_err(|e| Error::Generic(e.to_string()))
718-
{
719-
if let Ok(device) =
720-
PairingBitbox02WithLocalCache::<runtime::TokioRuntime>::connect(device, None)
721-
.await
722-
{
723-
if let Ok((device, _)) = device.wait_confirm().await {
724-
let mut bb02 = BitBox02::from(device).with_network(network);
725-
bb02 = bb02
726-
.with_policy(&ext_descriptor.as_str())
727-
.map_err(Error::HwiError)?;
728-
return Ok(Some(bb02.into()));
729-
}
730-
}
731-
}
732-
}
733-
if device_info.vendor_id() == coldcard::api::COINKITE_VID
734-
&& device_info.product_id() == coldcard::api::CKCC_PID
735-
{
736-
if let Some(sn) = device_info.serial_number() {
737-
if let Ok((cc, _)) = coldcard::api::Coldcard::open(&api, sn, None)
738-
.map_err(|e| Error::Generic(e.to_string()))
739-
{
740-
let mut hw = coldcard::Coldcard::from(cc);
741-
hw = hw.with_wallet_name(wallet_name.to_string());
742-
return Ok(Some(hw.into()));
701+
service.stop();
702+
return Ok(Some(device));
743703
}
744704
}
745705
}
706+
tokio::task::yield_now().await;
746707
}
747708

748-
for detected in Ledger::<TransportHID>::enumerate(&api) {
749-
let ext_descriptor = hwi_opts.ext_descriptor.clone().ok_or_else(|| {
750-
Error::Generic(
751-
"External descriptor required for connecting to ledger hardware device".to_string(),
752-
)
753-
})?;
754-
let wallet_name = hwi_opts.wallet.clone().ok_or_else(|| {
755-
Error::Generic(
756-
"Wallet name is required for connecting to ledger hardware device".to_string(),
757-
)
758-
})?;
759-
760-
if let Ok(mut device) = Ledger::<TransportHID>::connect(&api, detected) {
761-
device = device
762-
.with_wallet(wallet_name, &ext_descriptor, None)
763-
.map_err(Error::HwiError)?;
764-
return Ok(Some(device.into()));
765-
}
766-
}
767-
709+
service.stop();
768710
Ok(None)
769711
}

0 commit comments

Comments
 (0)