From 9fac6277f42b605200134e6a670cb3511edf80cc Mon Sep 17 00:00:00 2001 From: edouard Date: Tue, 14 Feb 2023 13:56:40 +0100 Subject: [PATCH] Sign with hot signer --- gui/src/app/state/spend/detail.rs | 98 +++++++++++++++++-------------- gui/src/app/view/hw.rs | 11 ++-- gui/src/app/view/message.rs | 1 + gui/src/app/view/spend/detail.rs | 54 ++++++++++++----- 4 files changed, 98 insertions(+), 66 deletions(-) diff --git a/gui/src/app/state/spend/detail.rs b/gui/src/app/state/spend/detail.rs index 2b7066a8..b568c6e6 100644 --- a/gui/src/app/state/spend/detail.rs +++ b/gui/src/app/state/spend/detail.rs @@ -25,16 +25,11 @@ trait Action { fn warning(&self) -> Option<&Error> { None } - fn load( - &self, - _wallet: Arc, - _daemon: Arc, - ) -> Command { + fn load(&self, _daemon: Arc) -> Command { Command::none() } fn update( &mut self, - _wallet: Arc, _daemon: Arc, _message: Message, _tx: &mut SpendTx, @@ -65,7 +60,7 @@ impl SpendTxState { pub fn load(&self, daemon: Arc) -> Command { if let Some(action) = &self.action { - action.load(self.wallet.clone(), daemon) + action.load(daemon) } else { Command::none() } @@ -86,14 +81,14 @@ impl SpendTxState { self.action = Some(Box::new(DeleteAction::default())); } view::SpendTxMessage::Sign => { - let action = SignAction::new(); - let cmd = action.load(self.wallet.clone(), daemon); + let action = SignAction::new(self.wallet.clone()); + let cmd = action.load(daemon); self.action = Some(Box::new(action)); return cmd; } view::SpendTxMessage::EditPsbt => { - let action = UpdateAction::new(self.tx.psbt.to_string()); - let cmd = action.load(self.wallet.clone(), daemon); + let action = UpdateAction::new(self.wallet.clone(), self.tx.psbt.to_string()); + let cmd = action.load(daemon); self.action = Some(Box::new(action)); return cmd; } @@ -105,34 +100,19 @@ impl SpendTxState { } _ => { if let Some(action) = self.action.as_mut() { - return action.update( - self.wallet.clone(), - daemon.clone(), - message, - &mut self.tx, - ); + return action.update(daemon.clone(), message, &mut self.tx); } } }, Message::Updated(Ok(_)) => { self.saved = true; if let Some(action) = self.action.as_mut() { - return action.update( - self.wallet.clone(), - daemon.clone(), - message, - &mut self.tx, - ); + return action.update(daemon.clone(), message, &mut self.tx); } } _ => { if let Some(action) = self.action.as_mut() { - return action.update( - self.wallet.clone(), - daemon.clone(), - message, - &mut self.tx, - ); + return action.update(daemon.clone(), message, &mut self.tx); } } }; @@ -166,7 +146,6 @@ pub struct SaveAction { impl Action for SaveAction { fn update( &mut self, - _wallet: Arc, daemon: Arc, message: Message, tx: &mut SpendTx, @@ -202,7 +181,6 @@ pub struct BroadcastAction { impl Action for BroadcastAction { fn update( &mut self, - _wallet: Arc, daemon: Arc, message: Message, tx: &mut SpendTx, @@ -246,7 +224,6 @@ pub struct DeleteAction { impl Action for DeleteAction { fn update( &mut self, - _wallet: Arc, daemon: Arc, message: Message, tx: &mut SpendTx, @@ -279,6 +256,7 @@ impl Action for DeleteAction { } pub struct SignAction { + wallet: Arc, chosen_hw: Option, processing: bool, hws: Vec, @@ -287,8 +265,9 @@ pub struct SignAction { } impl SignAction { - pub fn new() -> Self { + pub fn new(wallet: Arc) -> Self { Self { + wallet, chosen_hw: None, processing: false, hws: Vec::new(), @@ -303,16 +282,14 @@ impl Action for SignAction { self.error.as_ref() } - fn load( - &self, - wallet: Arc, - _daemon: Arc, - ) -> Command { - Command::perform(list_hws(wallet), Message::ConnectedHardwareWallets) + fn load(&self, _daemon: Arc) -> Command { + Command::perform( + list_hws(self.wallet.clone()), + Message::ConnectedHardwareWallets, + ) } fn update( &mut self, - wallet: Arc, daemon: Arc, message: Message, tx: &mut SpendTx, @@ -334,6 +311,13 @@ impl Action for SignAction { ); } } + Message::View(view::Message::Spend(view::SpendTxMessage::SelectHotSigner)) => { + self.processing = true; + return Command::perform( + sign_psbt_with_hot_signer(self.wallet.clone(), tx.psbt.clone()), + Message::Signed, + ); + } Message::Signed(res) => match res { Err(e) => self.error = Some(e), Ok((psbt, fingerprint)) => { @@ -350,7 +334,11 @@ impl Action for SignAction { Message::Updated(res) => match res { Ok(()) => { self.processing = false; - tx.sigs = wallet.main_descriptor.partial_spend_info(&tx.psbt).unwrap(); + tx.sigs = self + .wallet + .main_descriptor + .partial_spend_info(&tx.psbt) + .unwrap(); } Err(e) => self.error = Some(e), }, @@ -370,7 +358,7 @@ impl Action for SignAction { self.hws = Vec::new(); self.chosen_hw = None; self.error = None; - return self.load(wallet, daemon); + return self.load(daemon); } _ => {} }; @@ -380,6 +368,7 @@ impl Action for SignAction { view::spend::detail::sign_action( self.error.as_ref(), &self.hws, + self.wallet.signer.as_ref().map(|s| s.fingerprint()), self.processing, self.chosen_hw, &self.signed, @@ -395,6 +384,20 @@ async fn list_hws(wallet: Arc) -> Vec { .await } +async fn sign_psbt_with_hot_signer( + wallet: Arc, + psbt: Psbt, +) -> Result<(Psbt, Fingerprint), Error> { + if let Some(signer) = &wallet.signer { + let psbt = signer + .sign_psbt(psbt) + .map_err(|e| Error::HotSigner(format!("Hot signer failed to sign psbt: {}", e)))?; + Ok((psbt, signer.fingerprint())) + } else { + Err(Error::HotSigner("Hot signer not loaded".to_string())) + } +} + async fn sign_psbt( hw: std::sync::Arc, fingerprint: Fingerprint, @@ -405,6 +408,7 @@ async fn sign_psbt( } pub struct UpdateAction { + wallet: Arc, psbt: String, updated: form::Value, processing: bool, @@ -413,8 +417,9 @@ pub struct UpdateAction { } impl UpdateAction { - pub fn new(psbt: String) -> Self { + pub fn new(wallet: Arc, psbt: String) -> Self { Self { + wallet, psbt, updated: form::Value::default(), processing: false, @@ -440,7 +445,6 @@ impl Action for UpdateAction { fn update( &mut self, - wallet: Arc, daemon: Arc, message: Message, tx: &mut SpendTx, @@ -477,7 +481,11 @@ impl Action for UpdateAction { .extend(updated_input.partial_sigs.clone().into_iter()); } } - tx.sigs = wallet.main_descriptor.partial_spend_info(&tx.psbt).unwrap(); + tx.sigs = self + .wallet + .main_descriptor + .partial_spend_info(&tx.psbt) + .unwrap(); } Err(e) => self.error = e.into(), } diff --git a/gui/src/app/view/hw.rs b/gui/src/app/view/hw.rs index c32b8357..1410b1a1 100644 --- a/gui/src/app/view/hw.rs +++ b/gui/src/app/view/hw.rs @@ -74,12 +74,11 @@ pub fn hw_list_view( }) .push_maybe(if signed { Some( - Column::new().push( - Row::new() - .spacing(5) - .push(icon::circle_check_icon().style(color::SUCCESS)) - .push(text("Signed").style(color::SUCCESS)), - ), + Row::new() + .align_items(Alignment::Center) + .spacing(5) + .push(icon::circle_check_icon().style(color::SUCCESS)) + .push(text("Signed").style(color::SUCCESS)), ) } else { None diff --git a/gui/src/app/view/message.rs b/gui/src/app/view/message.rs index ceb85482..95afe49b 100644 --- a/gui/src/app/view/message.rs +++ b/gui/src/app/view/message.rs @@ -40,6 +40,7 @@ pub enum SpendTxMessage { Save, Confirm, Cancel, + SelectHotSigner, SelectHardwareWallet(usize), EditPsbt, PsbtEdited(String), diff --git a/gui/src/app/view/spend/detail.rs b/gui/src/app/view/spend/detail.rs index e28316ee..056012ec 100644 --- a/gui/src/app/view/spend/detail.rs +++ b/gui/src/app/view/spend/detail.rs @@ -714,6 +714,7 @@ pub fn inputs_and_outputs_view<'a>( pub fn sign_action<'a>( warning: Option<&Error>, hws: &'a [HardwareWallet], + signer: Option, processing: bool, chosen_hw: Option, signed: &[Fingerprint], @@ -722,12 +723,12 @@ pub fn sign_action<'a>( .push_maybe(warning.map(|w| warn(Some(w)))) .push(card::simple( Column::new() - .push(if !hws.is_empty() { + .push( Column::new() .push( Row::new() .push( - text("Select hardware wallet to sign with:") + text("Select signing device to sign with:") .bold() .width(Length::Fill), ) @@ -749,19 +750,42 @@ pub fn sign_action<'a>( )) }, )) - .width(Length::Fill) - } else { - Column::new() - .push( - Column::new() - .spacing(15) - .width(Length::Fill) - .push("Please connect a hardware wallet") - .push(button::border(None, "Refresh").on_press(Message::Reload)) - .align_items(Alignment::Center), - ) - .width(Length::Fill) - }) + .push_maybe(signer.map(|fingerprint| { + Button::new( + Row::new() + .align_items(Alignment::Center) + .push( + Column::new() + .width(Length::Fill) + .push(text("This computer").bold()) + .push( + text(format!("fingerprint: {}", fingerprint)) + .small(), + ) + .spacing(5) + .width(Length::Fill), + ) + .push_maybe(if signed.contains(&fingerprint) { + Some( + Row::new() + .align_items(Alignment::Center) + .spacing(5) + .push( + icon::circle_check_icon().style(color::SUCCESS), + ) + .push(text("Signed").style(color::SUCCESS)), + ) + } else { + None + }), + ) + .on_press(Message::Spend(SpendTxMessage::SelectHotSigner)) + .padding(10) + .style(button::Style::Border.into()) + .width(Length::Fill) + })) + .width(Length::Fill), + ) .spacing(20) .width(Length::Fill) .align_items(Alignment::Center),