diff --git a/gui/Cargo.toml b/gui/Cargo.toml index 14eb47b06..7e0a2a761 100644 --- a/gui/Cargo.toml +++ b/gui/Cargo.toml @@ -47,7 +47,7 @@ bitcoin_hashes = "0.12" reqwest = { version = "0.11", default-features=false, features = ["json", "rustls-tls"] } rust-ini = "0.19.0" -ledger_manager = { git = "https://github.com/pythcoiner/ledger_installer.git", branch = "liana", ref = "bdefcefad4a5c8d15cfaea2c1103effdc961fe18"} +ledger_manager = { git = "https://github.com/pythcoiner/ledger_installer.git", branch = "liana", ref = "53477ee2077590c461e605350aa0d76494a162d9"} [patch.crates-io] iced_style = { git = "https://github.com/edouardparis/iced", branch = "patch-0.12.3"} diff --git a/gui/src/app/message.rs b/gui/src/app/message.rs index 15b340a91..a0f16343f 100644 --- a/gui/src/app/message.rs +++ b/gui/src/app/message.rs @@ -45,6 +45,6 @@ pub enum Message { LabelsUpdated(Result>, Error>), BroadcastModal(Result, Error>), RbfModal(Box, bool, Result, Error>), - UpgradeLedger(String), + UpgradeLedger(String, Network), Upgrade(UpgradeMessage), } diff --git a/gui/src/app/state/psbt.rs b/gui/src/app/state/psbt.rs index 4b9c8f6d1..6e236cade 100644 --- a/gui/src/app/state/psbt.rs +++ b/gui/src/app/state/psbt.rs @@ -413,6 +413,7 @@ pub struct SignAction { signed: HashSet, is_saved: bool, display_modal: bool, + runtime: tokio::runtime::Handle, } impl SignAction { @@ -431,14 +432,15 @@ impl SignAction { signed, is_saved, display_modal: true, + runtime: tokio::runtime::Handle::current(), } } } impl Action for SignAction { fn subscription(&self) -> Subscription { - let mut subs = maybe_ledger_upgrade_subscription(&self.hws); - subs.push(self.hws.refresh().map(Message::HardwareWallets)); + let ( mut subs, upgrading_hws ) = maybe_ledger_upgrade_subscription(&self.hws, self.runtime.clone()); + subs.push(self.hws.refresh(upgrading_hws).map(Message::HardwareWallets)); Subscription::batch(subs) } @@ -526,8 +528,8 @@ impl Action for SignAction { self.error = Some(e.into()); } }, - Message::UpgradeLedger(id) => { - maybe_start_upgrade(id, &mut self.hws); + Message::UpgradeLedger(id, network) => { + maybe_start_upgrade(id, &mut self.hws, network); } Message::Upgrade(msg) => { update_upgrade_state(msg, &mut self.hws); diff --git a/gui/src/app/state/receive.rs b/gui/src/app/state/receive.rs index 49d6e5cb8..7ac882fbe 100644 --- a/gui/src/app/state/receive.rs +++ b/gui/src/app/state/receive.rs @@ -224,6 +224,7 @@ pub struct VerifyAddressModal { hws: HardwareWallets, address: Address, derivation_index: ChildNumber, + runtime: tokio::runtime::Handle, } impl VerifyAddressModal { @@ -240,6 +241,7 @@ impl VerifyAddressModal { hws: HardwareWallets::new(data_dir, network).with_wallet(wallet), address, derivation_index, + runtime: tokio::runtime::Handle::current(), } } } @@ -256,8 +258,13 @@ impl VerifyAddressModal { } fn subscription(&self) -> Subscription { - let mut subs = maybe_ledger_upgrade_subscription(&self.hws); - subs.push(self.hws.refresh().map(Message::HardwareWallets)); + let (mut subs, upgrading_hws) = + maybe_ledger_upgrade_subscription(&self.hws, self.runtime.clone()); + subs.push( + self.hws + .refresh(upgrading_hws) + .map(Message::HardwareWallets), + ); Subscription::batch(subs) } @@ -300,8 +307,8 @@ impl VerifyAddressModal { Command::none() } } - Message::UpgradeLedger(id) => { - maybe_start_upgrade(id, &mut self.hws); + Message::UpgradeLedger(id, network) => { + maybe_start_upgrade(id, &mut self.hws, network); Command::none() } Message::Upgrade(msg) => { diff --git a/gui/src/app/state/settings/wallet.rs b/gui/src/app/state/settings/wallet.rs index a904e49f8..bdb1b29bd 100644 --- a/gui/src/app/state/settings/wallet.rs +++ b/gui/src/app/state/settings/wallet.rs @@ -204,6 +204,7 @@ pub struct RegisterWalletModal { hws: HardwareWallets, registered: HashSet, processing: bool, + runtime: tokio::runtime::Handle, } impl RegisterWalletModal { @@ -220,6 +221,7 @@ impl RegisterWalletModal { wallet, processing: false, registered, + runtime: tokio::runtime::Handle::current(), } } } @@ -236,8 +238,13 @@ impl RegisterWalletModal { } fn subscription(&self) -> Subscription { - let mut subs = maybe_ledger_upgrade_subscription(&self.hws); - subs.push(self.hws.refresh().map(Message::HardwareWallets)); + let (mut subs, upgrading_hws) = + maybe_ledger_upgrade_subscription(&self.hws, self.runtime.clone()); + subs.push( + self.hws + .refresh(upgrading_hws) + .map(Message::HardwareWallets), + ); Subscription::batch(subs) } @@ -303,8 +310,8 @@ impl RegisterWalletModal { Command::none() } } - Message::UpgradeLedger(id) => { - maybe_start_upgrade(id, &mut self.hws); + Message::UpgradeLedger(id, network) => { + maybe_start_upgrade(id, &mut self.hws, network); Command::none() } Message::Upgrade(msg) => { diff --git a/gui/src/hw.rs b/gui/src/hw.rs index 54dc964df..afa535264 100644 --- a/gui/src/hw.rs +++ b/gui/src/hw.rs @@ -36,9 +36,6 @@ pub enum HardwareWallet { kind: DeviceKind, version: Option, reason: UnsupportedReason, - upgrade_in_progress: bool, - upgrade_step: Option, - upgrade_log: Vec, }, Locked { id: String, @@ -55,6 +52,10 @@ pub enum HardwareWallet { version: Option, registered: Option, alias: Option, + upgrade_in_progress: bool, + upgrade_step: Option, + upgrade_log: Vec, + upgrade_testnet: bool, }, } @@ -86,6 +87,10 @@ impl HardwareWallet { version, registered: None, alias: aliases.and_then(|aliases| aliases.get(&fingerprint).cloned()), + upgrade_in_progress: false, + upgrade_step: None, + upgrade_log: Vec::new(), + upgrade_testnet: false, }) } @@ -118,7 +123,7 @@ impl HardwareWallet { } pub fn is_upgrade_in_progress(&self) -> bool { - if let Self::Unsupported { + if let Self::Supported { upgrade_in_progress, .. } = self @@ -129,26 +134,32 @@ impl HardwareWallet { } } - pub fn start_upgrade(&mut self) { - if let Self::Unsupported { + pub fn start_upgrade(&mut self, network: Network) { + if let Self::Supported { upgrade_in_progress, upgrade_step, + upgrade_testnet, + id, .. } = self { + log::info!("HardwareWallet.start_upgrade({}, {})", id, network); *upgrade_step = None; *upgrade_in_progress = true; + *upgrade_testnet = network != Network::Bitcoin; } } pub fn upgrade_ended(&mut self) { - if let Self::Unsupported { + if let Self::Supported { upgrade_in_progress, upgrade_step, upgrade_log, + id, .. } = self { + log::info!("HardwareWallet.upgrade_ended({})", id); *upgrade_in_progress = false; *upgrade_step = None; *upgrade_log = Vec::new(); @@ -156,25 +167,31 @@ impl HardwareWallet { } pub fn upgrade_failed(&mut self) { - if let Self::Unsupported { + if let Self::Supported { upgrade_in_progress, upgrade_step, + id, .. } = self { + log::info!("HardwareWallet.upgrade_failed({})", id); *upgrade_in_progress = false; *upgrade_step = Some(InstallStep::Error("Failed to install app".into())); } } pub fn push_log(&mut self, log: String) { - if let Self::Unsupported { upgrade_log, .. } = self { + if let Self::Supported { + upgrade_log, id, .. + } = self + { + log::info!("HardwareWallet.push_log({}, {})", log, id); upgrade_log.push(log); } } pub fn logs(&self) -> Vec { - if let Self::Unsupported { upgrade_log, .. } = self { + if let Self::Supported { upgrade_log, .. } = self { upgrade_log.clone() } else { Vec::new() @@ -324,6 +341,10 @@ impl HardwareWallets { version: None, registered: Some(registered), alias: None, + upgrade_in_progress: false, + upgrade_log: Vec::new(), + upgrade_step: None, + upgrade_testnet: false, }) } else { Ok(HardwareWallet::Unsupported { @@ -333,9 +354,6 @@ impl HardwareWallets { reason: UnsupportedReason::NotPartOfWallet( fingerprint, ), - upgrade_in_progress: false, - upgrade_log: Vec::new(), - upgrade_step: None, }) } } else { @@ -347,6 +365,10 @@ impl HardwareWallets { version: None, registered: Some(registered), alias: None, + upgrade_in_progress: false, + upgrade_log: Vec::new(), + upgrade_step: None, + upgrade_testnet: false, }) } }, @@ -413,7 +435,7 @@ impl HardwareWallets { } } - pub fn refresh(&self) -> iced::Subscription { + pub fn refresh(&self, upgrading_hws: Vec) -> iced::Subscription { iced::subscription::unfold( format!("refresh-{}", self.network), State { @@ -423,6 +445,7 @@ impl HardwareWallets { connected_supported_hws: Vec::new(), api: None, datadir_path: self.datadir_path.clone(), + upgrading_hws, }, refresh, ) @@ -434,6 +457,7 @@ struct State { keys_aliases: HashMap, wallet: Option>, connected_supported_hws: Vec, + upgrading_hws: Vec, api: Option, datadir_path: PathBuf, } @@ -590,6 +614,10 @@ async fn refresh(mut state: State) -> (HardwareWalletMessage, State) { version, registered: Some(registered), alias: state.keys_aliases.get(&fingerprint).cloned(), + upgrade_in_progress: false, + upgrade_log: Vec::new(), + upgrade_step: None, + upgrade_testnet: false, }); } else { hws.push(HardwareWallet::Unsupported { @@ -599,9 +627,6 @@ async fn refresh(mut state: State) -> (HardwareWalletMessage, State) { reason: UnsupportedReason::Version { minimal_supported_version: "2.1.0", }, - upgrade_in_progress: false, - upgrade_log: Vec::new(), - upgrade_step: None, }); } } @@ -613,9 +638,6 @@ async fn refresh(mut state: State) -> (HardwareWalletMessage, State) { reason: UnsupportedReason::Version { minimal_supported_version: "2.1.0", }, - upgrade_in_progress: false, - upgrade_log: Vec::new(), - upgrade_step: None, }); } } @@ -698,7 +720,7 @@ async fn refresh(mut state: State) -> (HardwareWalletMessage, State) { } for detected in ledger::Ledger::::enumerate(api) { let id = ledger_id(detected.path(), detected.vendor_id(), detected.product_id()); - if state.connected_supported_hws.contains(&id) { + if state.connected_supported_hws.contains(&id) || state.upgrading_hws.contains(&id) { still.push(id); continue; } @@ -732,6 +754,10 @@ async fn refresh(mut state: State) -> (HardwareWalletMessage, State) { version, registered: Some(registered), alias: state.keys_aliases.get(&fingerprint).cloned(), + upgrade_in_progress: false, + upgrade_log: Vec::new(), + upgrade_step: None, + upgrade_testnet: false, }); } else { hws.push(HardwareWallet::Unsupported { @@ -741,9 +767,6 @@ async fn refresh(mut state: State) -> (HardwareWalletMessage, State) { reason: UnsupportedReason::Version { minimal_supported_version: "2.1.0", }, - upgrade_in_progress: false, - upgrade_log: Vec::new(), - upgrade_step: None, }); } } @@ -755,9 +778,6 @@ async fn refresh(mut state: State) -> (HardwareWalletMessage, State) { reason: UnsupportedReason::Version { minimal_supported_version: "2.1.0", }, - upgrade_in_progress: false, - upgrade_log: Vec::new(), - upgrade_step: None, }); } }, @@ -785,9 +805,6 @@ async fn refresh(mut state: State) -> (HardwareWalletMessage, State) { kind: *kind, version: version.clone(), reason: UnsupportedReason::NotPartOfWallet(*fingerprint), - upgrade_in_progress: false, - upgrade_log: Vec::new(), - upgrade_step: None, }; } } @@ -829,9 +846,6 @@ async fn handle_jade_device( kind: device.device_kind(), version, reason: UnsupportedReason::WrongNetwork, - upgrade_in_progress: false, - upgrade_log: Vec::new(), - upgrade_step: None, }) } else { match info.jade_state { @@ -854,9 +868,6 @@ async fn handle_jade_device( kind, version, reason: UnsupportedReason::WrongNetwork, - upgrade_in_progress: false, - upgrade_log: Vec::new(), - upgrade_step: None, }); } Err(e) => { @@ -878,6 +889,10 @@ async fn handle_jade_device( version, registered: Some(registered), alias, + upgrade_in_progress: false, + upgrade_log: Vec::new(), + upgrade_step: None, + upgrade_testnet: false, }) } else { Ok(HardwareWallet::Unsupported { @@ -885,9 +900,6 @@ async fn handle_jade_device( kind, version, reason: UnsupportedReason::NotPartOfWallet(fingerprint), - upgrade_in_progress: false, - upgrade_log: Vec::new(), - upgrade_step: None, }) } } else { @@ -899,6 +911,10 @@ async fn handle_jade_device( version, registered: Some(false), alias, + upgrade_in_progress: false, + upgrade_log: Vec::new(), + upgrade_step: None, + upgrade_testnet: false, }) } } @@ -979,10 +995,11 @@ pub fn is_compatible_with_tapminiscript( } pub fn ledger_need_taproot_upgrade(device_kind: &DeviceKind, version: &Option) -> bool { - if let (Some(version), DeviceKind::Ledger) = (version, device_kind) { - ledger_version_supported(Some(version)) - && !is_compatible_with_tapminiscript(device_kind, Some(version)) - } else { - false - } + // if let (Some(version), DeviceKind::Ledger) = (version, device_kind) { + // ledger_version_supported(Some(version)) + // && !is_compatible_with_tapminiscript(device_kind, Some(version)) + // } else { + // false + // } + true } diff --git a/gui/src/installer/message.rs b/gui/src/installer/message.rs index 118174de4..c0a99062b 100644 --- a/gui/src/installer/message.rs +++ b/gui/src/installer/message.rs @@ -1,4 +1,7 @@ -use liana::miniscript::{bitcoin::bip32::Fingerprint, DescriptorPublicKey}; +use liana::miniscript::{ + bitcoin::{bip32::Fingerprint, Network}, + DescriptorPublicKey, +}; use std::path::PathBuf; use super::Error; @@ -38,7 +41,7 @@ pub enum Message { WalletRegistered(Result<(Fingerprint, Option<[u8; 32]>), Error>), MnemonicWord(usize, String), ImportMnemonic(bool), - UpgradeLedger(String), + UpgradeLedger(String, Network), Upgrade(UpgradeMessage), } diff --git a/gui/src/installer/mod.rs b/gui/src/installer/mod.rs index 89bc61097..d3e2eeb1d 100644 --- a/gui/src/installer/mod.rs +++ b/gui/src/installer/mod.rs @@ -140,6 +140,7 @@ impl Installer { } pub fn update(&mut self, message: Message) -> Command { + // log::info!("Installer.update({:?})", message); match message { Message::CreateWallet => { self.steps = vec![ diff --git a/gui/src/installer/step/descriptor.rs b/gui/src/installer/step/descriptor.rs index 1bd787a7c..b4636c7eb 100644 --- a/gui/src/installer/step/descriptor.rs +++ b/gui/src/installer/step/descriptor.rs @@ -741,6 +741,7 @@ pub struct EditXpubModal { hot_signer: Arc>, hot_signer_fingerprint: Fingerprint, chosen_signer: Option<(Fingerprint, Option, Option)>, + runtime: tokio::runtime::Handle, } impl EditXpubModal { @@ -794,6 +795,7 @@ impl EditXpubModal { hot_signer_fingerprint, hot_signer, duplicate_master_fg: false, + runtime: tokio::runtime::Handle::current(), } } fn load(&self) -> Command { @@ -807,6 +809,7 @@ impl DescriptorEditModal for EditXpubModal { } fn update(&mut self, hws: &mut HardwareWallets, message: Message) -> Command { + log::info!("EditXpubModal.update({:?})", message); // Reset these fields. // the fonction will setup them again if something is wrong self.duplicate_master_fg = false; @@ -992,8 +995,8 @@ impl DescriptorEditModal for EditXpubModal { } } }, - Message::UpgradeLedger(id) => { - maybe_start_upgrade(id, hws); + Message::UpgradeLedger(id, network) => { + maybe_start_upgrade(id, hws, network); } Message::Upgrade(msg) => { update_upgrade_state(msg, hws); @@ -1004,8 +1007,9 @@ impl DescriptorEditModal for EditXpubModal { } fn subscription(&self, hws: &HardwareWallets) -> Subscription { - let mut subs = maybe_ledger_upgrade_subscription(hws); - subs.push(hws.refresh().map(Message::HardwareWallets)); + let (mut subs, upgrading_hws) = + maybe_ledger_upgrade_subscription(hws, self.runtime.clone()); + subs.push(hws.refresh(upgrading_hws).map(Message::HardwareWallets)); Subscription::batch(subs) } @@ -1034,6 +1038,7 @@ impl DescriptorEditModal for EditXpubModal { && self.form_xpub.valid && !self.form_xpub.value.is_empty(), self.device_must_support_tapminiscript, + self.network, )) } }) @@ -1206,6 +1211,7 @@ pub struct RegisterDescriptor { /// whether a signing device is used, to explicit this step is not required if the user isn't /// using a signing device. created_desc: bool, + runtime: tokio::runtime::Handle, } impl RegisterDescriptor { @@ -1219,6 +1225,7 @@ impl RegisterDescriptor { registered: Default::default(), error: Default::default(), done: Default::default(), + runtime: tokio::runtime::Handle::current(), } } @@ -1298,8 +1305,8 @@ impl Step for RegisterDescriptor { Message::UserActionDone(done) => { self.done = done; } - Message::UpgradeLedger(id) => { - maybe_start_upgrade(id, hws); + Message::UpgradeLedger(id, network) => { + maybe_start_upgrade(id, hws, network); } Message::Upgrade(msg) => { update_upgrade_state(msg, hws); @@ -1318,8 +1325,9 @@ impl Step for RegisterDescriptor { true } fn subscription(&self, hws: &HardwareWallets) -> Subscription { - let mut subs = maybe_ledger_upgrade_subscription(hws); - subs.push(hws.refresh().map(Message::HardwareWallets)); + let (mut subs, upgrading_hws) = + maybe_ledger_upgrade_subscription(hws, self.runtime.clone()); + subs.push(hws.refresh(upgrading_hws).map(Message::HardwareWallets)); Subscription::batch(subs) } fn load(&self) -> Command { diff --git a/gui/src/installer/step/share_xpubs.rs b/gui/src/installer/step/share_xpubs.rs index 40dd1f5c0..f168ea465 100644 --- a/gui/src/installer/step/share_xpubs.rs +++ b/gui/src/installer/step/share_xpubs.rs @@ -150,7 +150,7 @@ impl Step for ShareXpubs { } fn subscription(&self, hws: &HardwareWallets) -> Subscription { - hws.refresh().map(Message::HardwareWallets) + hws.refresh(Vec::new()).map(Message::HardwareWallets) } fn apply(&mut self, ctx: &mut Context) -> bool { diff --git a/gui/src/installer/view.rs b/gui/src/installer/view.rs index 135f99f24..15e38b48d 100644 --- a/gui/src/installer/view.rs +++ b/gui/src/installer/view.rs @@ -1,24 +1,29 @@ -use async_hwi::utils::extract_keys_and_template; -use iced::widget::{ - checkbox, container, pick_list, radio, scrollable, scrollable::Properties, slider, Button, - Space, TextInput, +use async_hwi::{utils::extract_keys_and_template, DeviceKind}; +use iced::{ + alignment, + widget::{ + checkbox, container, pick_list, progress_bar, radio, scrollable, scrollable::Properties, + slider, Button, Space, TextInput, + }, + Alignment, Length, }; -use iced::{alignment, widget::progress_bar, Alignment, Length}; -use async_hwi::DeviceKind; -use liana_ui::component::{ - hw::{ledger_need_upgrade, ledger_upgrading}, - text, +use liana::{ + descriptors::LianaDescriptor, + miniscript::bitcoin::{self, bip32::Fingerprint, Network}, +}; +use std::{ + net::{Ipv4Addr, Ipv6Addr}, + path::PathBuf, + {collections::HashSet, str::FromStr}, }; -use std::net::{Ipv4Addr, Ipv6Addr}; -use std::path::PathBuf; -use std::{collections::HashSet, str::FromStr}; -use liana::miniscript::bitcoin::{self, bip32::Fingerprint}; use liana_ui::{ color, component::{ - button, card, collapse, form, hw, separation, + button, card, collapse, form, hw, + hw::{ledger_need_upgrade, ledger_upgrading}, + separation, text, text::{h3, p1_regular, text, Text}, tooltip, }, @@ -652,6 +657,14 @@ pub fn register_descriptor<'a>( done: bool, created_desc: bool, ) -> Element<'a, Message> { + let network = if LianaDescriptor::from_str(&descriptor) + .expect("Descriptor should be valid") + .all_xpubs_net_is(Network::Bitcoin) + { + Network::Bitcoin + } else { + Network::Testnet + }; let displayed_descriptor = if let Ok((template, keys)) = extract_keys_and_template::(&descriptor) { @@ -765,6 +778,7 @@ pub fn register_descriptor<'a>( .map(|fg| registered.contains(&fg)) .unwrap_or(false), false, + network, )) }), ) @@ -1716,6 +1730,7 @@ pub fn hw_list_view( processing: bool, selected: bool, device_must_support_taproot: bool, + network: Network, ) -> Element { let mut bttn = Button::new(match hw { HardwareWallet::Supported { @@ -1723,22 +1738,22 @@ pub fn hw_list_view( version, fingerprint, alias, + id, .. } => { if chosen && processing { hw::processing_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref()) } else if selected { hw::selected_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref()) - } else if device_must_support_taproot - && !is_compatible_with_tapminiscript(kind, version.as_ref()) - { - hw::warning_hardware_wallet( - kind, - version.as_ref(), - fingerprint, - alias.as_ref(), - "Device firmware version does not support taproot miniscript", - ) + } else if device_must_support_taproot && ledger_need_taproot_upgrade(kind, version) { + match hw.is_upgrade_in_progress() { + false => ledger_need_upgrade( + kind, + version.clone(), + Message::UpgradeLedger(id.clone(), network), + ), + true => ledger_upgrading(kind, version.clone(), hw.logs()), + } } else { hw::supported_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref()) } @@ -1747,7 +1762,6 @@ pub fn hw_list_view( version, kind, reason, - id, .. } => match reason { UnsupportedReason::NotPartOfWallet(fg) => { @@ -1756,20 +1770,7 @@ pub fn hw_list_view( UnsupportedReason::WrongNetwork => { hw::wrong_network_hardware_wallet(&kind.to_string(), version.as_ref()) } - _ => { - if ledger_need_taproot_upgrade(kind, version) { - match hw.is_upgrade_in_progress() { - false => ledger_need_upgrade( - kind, - version.clone(), - Message::UpgradeLedger(id.clone()), - ), - true => ledger_upgrading(kind, version.clone(), hw.logs()), - } - } else { - hw::unsupported_hardware_wallet(&kind.to_string(), version.as_ref()) - } - } + _ => hw::unsupported_hardware_wallet(&kind.to_string(), version.as_ref()), }, HardwareWallet::Locked { kind, pairing_code, .. diff --git a/gui/src/ledger_upgrade.rs b/gui/src/ledger_upgrade.rs index d3cdf5cbc..d899098fc 100644 --- a/gui/src/ledger_upgrade.rs +++ b/gui/src/ledger_upgrade.rs @@ -1,7 +1,8 @@ use std::{ + fmt::Debug, marker::PhantomData, sync::mpsc::{self, TryRecvError}, - task::Poll, + time::Duration, }; use crate::{ @@ -9,11 +10,12 @@ use crate::{ hw::{ledger_id, HardwareWallet, HardwareWallets}, installer::Message as InstallerMessage, }; -use iced::{futures::Stream, Subscription}; +use iced::{subscription, Subscription}; use ledger_manager::{ ledger_transport_hidapi::TransportNativeHID, utils::{install_app, ledger_api, InstallStep, Step}, }; +use liana::miniscript::bitcoin::Network; #[derive(Debug, Clone)] pub struct UpgradeProgress { @@ -24,9 +26,11 @@ pub struct UpgradeProgress { #[derive(Debug, Clone)] pub enum UpgradeMessage { + None { id: String }, Progress(UpgradeProgress), Completed { id: String }, Error { id: String, msg: String }, + Stopped { id: String }, } impl UpgradeMessage { @@ -35,6 +39,8 @@ impl UpgradeMessage { UpgradeMessage::Progress(UpgradeProgress { id, .. }) => id, UpgradeMessage::Completed { id } => id, UpgradeMessage::Error { id, .. } => id, + UpgradeMessage::None { id } => id, + UpgradeMessage::Stopped { id } => id, } } @@ -55,6 +61,7 @@ impl UpgradeMessage { hw.push_log(msg); hw.upgrade_failed(); } + _ => {} } } } @@ -71,22 +78,45 @@ impl From for InstallerMessage { } } -#[allow(unused)] +pub enum State { + Idle, + Running, + Stopped, +} + pub struct UpgradeState> { id: String, + state: State, step: InstallStep, chunks: usize, percent: f32, + testnet: bool, receiver: mpsc::Receiver, - stop: bool, + sender: mpsc::Sender, _marker: PhantomData, } #[allow(unused)] impl> UpgradeState { - pub fn new(id: String, testnet: bool) -> Option { + pub fn new(id: String, testnet: bool) -> Self { let (sender, receiver) = mpsc::channel(); - if let Some(api) = connect(id.clone()) { + UpgradeState:: { + id, + step: InstallStep::Info("Not yet started".into()), + chunks: 0, + percent: 0.0, + receiver, + _marker: Default::default(), + state: State::Idle, + sender, + testnet, + } + } + + fn start(&mut self) { + if let Some(api) = connect(self.id.clone()) { + let sender = self.sender.clone(); + let testnet = self.testnet; tokio::spawn(async move { install_app( &api, @@ -96,23 +126,16 @@ impl> UpgradeState { testnet, ) }); - - UpgradeState:: { - id, - step: InstallStep::Info("Not yet started".into()), - chunks: 0, - percent: 0.0, - receiver, - stop: false, - _marker: Default::default(), - } - .into() + self.state = State::Running; } else { - None + let _ = self + .sender + .send(InstallStep::Error("Fail to get ledger HID".into())); } } fn update(&mut self, msg: InstallStep) -> T { + log::info!("UpgradeState.update({:?})", msg); if !matches!(msg, InstallStep::Info(_)) { self.step = msg.clone(); } @@ -132,14 +155,14 @@ impl> UpgradeState { match msg { InstallStep::Completed => { - self.stop = true; + self.state = State::Stopped; UpgradeMessage::Completed { id: self.id.clone(), } .into() } InstallStep::Error(e) => { - self.stop = true; + self.state = State::Stopped; UpgradeMessage::Error { id: self.id.clone(), msg: e, @@ -156,32 +179,34 @@ impl> UpgradeState { } } -impl + Unpin> Stream for UpgradeState { - type Item = T; - - fn poll_next( - self: std::pin::Pin<&mut Self>, - _cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - let state = self.get_mut(); - if state.stop { - Poll::Ready(None) - } else { - match state.receiver.try_recv() { - Ok(msg) => { - let ret = state.update(msg); - Poll::Ready(Some(ret)) - } - Err(TryRecvError::Empty) => Poll::Pending, - Err(TryRecvError::Disconnected) => { - let msg = InstallStep::Error("Disconnected".into()); - let ret = state.update(msg); - Poll::Ready(Some(ret)) - } - } - } - } -} +// impl + Unpin + Debug> Stream for UpgradeState { +// type Item = T; +// +// fn poll_next( +// self: std::pin::Pin<&mut Self>, +// _cx: &mut std::task::Context<'_>, +// ) -> std::task::Poll> { +// let state = self.get_mut(); +// if state.stop { +// Poll::Ready(None) +// } else { +// match state.receiver.try_recv() { +// Ok(msg) => { +// log::info!("{:?}", msg); +// let ret = state.update(msg.clone()); +// Poll::Ready(Some(ret)) +// } +// Err(TryRecvError::Empty) => Poll::Pending, +// Err(TryRecvError::Disconnected) => { +// log::info!("UpgradeState: Disconnected!"); +// let msg = InstallStep::Error("Disconnected".into()); +// let ret = state.update(msg); +// Poll::Ready(Some(ret)) +// } +// } +// } +// } +// } fn connect(id: String) -> Option { if let Ok(api) = ledger_api() { @@ -194,10 +219,10 @@ fn connect(id: String) -> Option { None } -pub fn maybe_start_upgrade(id: String, hws: &mut HardwareWallets) { +pub fn maybe_start_upgrade(id: String, hws: &mut HardwareWallets, network: Network) { if let Some(hw) = hws.list.iter_mut().find(|h| h.id() == &id) { if !hw.is_upgrade_in_progress() { - hw.start_upgrade() + hw.start_upgrade(network) } } } @@ -208,29 +233,33 @@ pub fn update_upgrade_state(msg: UpgradeMessage, hws: &mut HardwareWallets) { } } -pub fn maybe_ledger_upgrade_subscription + Unpin + Send + 'static>( +pub fn maybe_ledger_upgrade_subscription< + T: From + Debug + Unpin + Send + 'static, +>( hws: &HardwareWallets, -) -> Vec> { - hws.list + _runtime: tokio::runtime::Handle, +) -> (Vec>, Vec) { + let mut upgrading_hws = Vec::new(); + let subs: Vec<_> = hws + .list .iter() .filter_map(|hw| { - if let HardwareWallet::Unsupported { + // log::info!("{:?}", hw); + if let HardwareWallet::Supported { upgrade_in_progress, + upgrade_testnet, upgrade_step, id, .. } = hw { if *upgrade_in_progress && upgrade_step.is_none() { - // TODO: handle test app - #[allow(clippy::manual_map)] - match UpgradeState::new(id.clone(), false) { - Some(state) => Some(iced::subscription::run_with_id(id.clone(), state)), - None => { - // TODO: handle error - None - } - } + upgrading_hws.push(id.clone()); + Some(subscription::unfold( + id.clone(), + UpgradeState::new(id.clone(), *upgrade_testnet), + ledger_subscription, + )) } else { None } @@ -238,5 +267,48 @@ pub fn maybe_ledger_upgrade_subscription + Unpin + Send None } }) - .collect() + .collect(); + // log::info!("{} subscriptions", subs.len()); + (subs, upgrading_hws) +} + +async fn ledger_subscription>( + mut state: UpgradeState, +) -> (Message, UpgradeState) { + fn poll>(mut state: UpgradeState) -> (Option, UpgradeState) { + match &state.state { + State::Idle => { + state.start(); + (Some(state.update(InstallStep::NotStarted)), state) + } + State::Running => match state.receiver.try_recv() { + Ok(msg) => (Some(state.update(msg)), state), + Err(TryRecvError::Empty) => (None, state), + Err(TryRecvError::Disconnected) => { + let error = InstallStep::Error("Disconnected".into()); + (Some(state.update(error)), state) + } + }, + State::Stopped => ( + Some( + UpgradeMessage::Stopped { + id: state.id.clone(), + } + .into(), + ), + state, + ), + } + } + + loop { + let (m, s) = poll(state); + if let Some(message) = m { + return (message, s); + } else { + tokio::time::sleep(Duration::from_millis(500)).await; + log::info!("waiting for message"); + state = s; + } + } }