diff --git a/gui/Cargo.lock b/gui/Cargo.lock index edbd1895..823994e1 100644 --- a/gui/Cargo.lock +++ b/gui/Cargo.lock @@ -2415,7 +2415,7 @@ dependencies = [ [[package]] name = "liana" version = "2.0.0" -source = "git+https://github.com/wizardsardine/liana?branch=master#42578609e2ed340d277d75c747c74449308acbb7" +source = "git+https://github.com/wizardsardine/liana?branch=master#87555a8da50702ebec04dbe876e7055432ca805a" dependencies = [ "backtrace", "bip39", diff --git a/gui/src/app/error.rs b/gui/src/app/error.rs index 89fad1e9..a7944e40 100644 --- a/gui/src/app/error.rs +++ b/gui/src/app/error.rs @@ -1,7 +1,7 @@ use std::convert::From; use std::io::ErrorKind; -use liana::config::ConfigError; +use liana::{config::ConfigError, descriptors::LianaDescError}; use crate::{ app::{settings::SettingsError, wallet::WalletError}, @@ -15,6 +15,7 @@ pub enum Error { Daemon(DaemonError), Unexpected(String), HardwareWallet(async_hwi::Error), + Desc(LianaDescError), } impl std::fmt::Display for Error { @@ -48,6 +49,7 @@ impl std::fmt::Display for Error { }, Self::Unexpected(e) => write!(f, "Unexpected error: {}", e), Self::HardwareWallet(e) => write!(f, "{}", e), + Self::Desc(e) => write!(f, "Liana descriptor error: {}", e), } } } diff --git a/gui/src/app/state/psbt.rs b/gui/src/app/state/psbt.rs index 98a421bf..ead5d3aa 100644 --- a/gui/src/app/state/psbt.rs +++ b/gui/src/app/state/psbt.rs @@ -365,7 +365,7 @@ impl Action for SignAction { self.processing = true; let psbt = tx.psbt.clone(); return Command::perform( - sign_psbt(device.clone(), *fingerprint, psbt), + sign_psbt(self.wallet.clone(), device.clone(), *fingerprint, psbt), Message::Signed, ); } @@ -449,11 +449,39 @@ async fn sign_psbt_with_hot_signer( } async fn sign_psbt( + wallet: Arc, hw: std::sync::Arc, fingerprint: Fingerprint, mut psbt: Psbt, ) -> Result<(Psbt, Fingerprint), Error> { - hw.sign_tx(&mut psbt).await.map_err(Error::from)?; + // 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 + // derivation for the other paths. + if matches!(hw.device_kind(), async_hwi::DeviceKind::BitBox02) { + // 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 + // signatures back into the original PSBT. + let mut pruned_psbt = wallet + .main_descriptor + .prune_bip32_derivs_last_avail(psbt.clone()) + .map_err(Error::Desc)?; + hw.sign_tx(&mut pruned_psbt).await.map_err(Error::from)?; + for (i, psbt_in) in psbt.inputs.iter_mut().enumerate() { + if let Some(pruned_psbt_in) = pruned_psbt.inputs.get_mut(i) { + psbt_in + .partial_sigs + .append(&mut pruned_psbt_in.partial_sigs); + } else { + log::error!( + "Not all PSBT inputs are present in the pruned psbt. Pruned psbt: '{}'.", + &pruned_psbt + ); + } + } + } else { + hw.sign_tx(&mut psbt).await.map_err(Error::from)?; + } Ok((psbt, fingerprint)) } diff --git a/gui/src/app/view/warning.rs b/gui/src/app/view/warning.rs index a07b18aa..722178a2 100644 --- a/gui/src/app/view/warning.rs +++ b/gui/src/app/view/warning.rs @@ -37,6 +37,7 @@ impl From<&Error> for WarningMessage { }, Error::Unexpected(_) => WarningMessage("Unknown error".to_string()), Error::HardwareWallet(_) => WarningMessage("Hardware wallet error".to_string()), + Error::Desc(e) => WarningMessage(format!("Descriptor analysis error: '{}'.", e)), } } }