Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 102 additions & 6 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion liana-gui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ path = "src/main.rs"

[dependencies]
async-trait = { workspace = true }
async-hwi = { workspace = true }
async-hwi = { git = "https://github.com/romanz/async-hwi", branch = "trezor-miniscript" }
liana = { path = "../liana" }
liana-connect = { workspace = true }
lianad = { path = "../lianad", default-features = false, features = ["nonblocking_shutdown"] }
Expand Down
6 changes: 3 additions & 3 deletions liana-gui/src/app/state/psbt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -659,10 +659,10 @@ async fn sign_psbt(
hw: std::sync::Arc<dyn async_hwi::HWI + Send + Sync>,
mut psbt: Psbt,
) -> Result<Psbt, Error> {
// The BitBox02 is only going to produce a signature for a single key in the Script. In order
// to make sure it doesn't sign for a public key from another spending path we remove the BIP32
// Some devices are only going to produce a signature for a single key in the Script. In order
// to make sure they don't sign for a public key from another spending path we remove the BIP32
// derivation for the other paths.
if matches!(hw.device_kind(), async_hwi::DeviceKind::BitBox02) {
if hw.device_kind().requires_psbt_pruning() {
// We need to make sure we don't prune the BIP32 derivations from the original PSBT (which
// would end up being updated in the daemon's database and erase the previously unpruned
// one). To this end we create a new, pruned, psbt we use for signing and then merge its
Expand Down
79 changes: 77 additions & 2 deletions liana-gui/src/hw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ use async_hwi::{
bitbox::{api::runtime, BitBox02, PairingBitbox02},
coldcard,
jade::{self, Jade},
ledger, specter, DeviceKind, Error as HWIError, Version, HWI,
ledger, specter,
trezor::{self, TrezorClient, WalletPolicy},
DeviceKind, Error as HWIError, Version, HWI,
};
use iced::futures::{SinkExt, Stream};
use liana::miniscript::bitcoin::{bip32::Fingerprint, hashes::hex::FromHex, Network};
use serde::{Deserialize, Serialize};
use tracing::{debug, warn};
use tracing::{debug, error, warn};

#[derive(Debug, Clone)]
pub enum UnsupportedReason {
Expand Down Expand Up @@ -413,6 +415,35 @@ fn refresh(mut state: State) -> impl Stream<Item = HardwareWalletMessage> {

let mut hws: Vec<HardwareWallet> = Vec::new();
let mut still: Vec<String> = Vec::new();

for device in TrezorClient::find_devices() {
let id = format!("{:?}", device);
if state.connected_supported_hws.contains(&id) {
still.push(id);
continue;
}
let client = match trezor::TrezorClient::connect(device, state.network) {
Ok(client) => client,
Err(err) => {
error!("{} connection failed: {}", id, err);
continue;
}
};
match handle_trezor_device(
id,
client,
state.wallet.as_ref().map(|w| w.as_ref()),
&state.keys_aliases,
)
.await
{
Ok(hw) => hws.push(hw),
Err(e) => {
error!("Failed to create a wallet: {}", e);
}
}
}

match specter::SpecterSimulator::try_connect().await {
Ok(device) => {
let id = "specter-simulator".to_string();
Expand Down Expand Up @@ -714,6 +745,50 @@ fn refresh(mut state: State) -> impl Stream<Item = HardwareWalletMessage> {
})
}

async fn handle_trezor_device(
id: String,
mut device: TrezorClient,
wallet: Option<&Wallet>,
keys_aliases: &HashMap<Fingerprint, String>,
) -> Result<HardwareWallet, HWIError> {
// TODO: handle the case where the device is not supported
match (
device.get_master_fingerprint().await,
device.get_version().await,
) {
(Ok(fingerprint), Ok(version)) => {
let mut registered = false;
if let Some(w) = &wallet {
if let Some(cfg) = w
.hardware_wallets
.iter()
.find(|cfg| cfg.fingerprint == fingerprint)
{
let policy = w.main_descriptor.to_string();
let wallet = WalletPolicy::new(&w.name, &policy, cfg.token());
device = device.with_wallet(wallet)?;
registered = true;
}
}
Ok(HardwareWallet::Supported {
id,
device: Arc::new(device),
kind: DeviceKind::Trezor,
fingerprint,
version: Some(version),
registered: Some(registered),
alias: keys_aliases.get(&fingerprint).cloned(),
})
}
(_, _) => Ok(HardwareWallet::Unsupported {
id,
kind: DeviceKind::Trezor,
version: None,
reason: UnsupportedReason::AppIsNotOpen,
}),
}
}

async fn handle_ledger_device<'a, T: async_hwi::ledger::Transport + Sync + Send + 'static>(
id: String,
mut device: ledger::Ledger<T>,
Expand Down
Loading