From 1b7d96eff577710b63f6703cf423ff1c5bea2dc6 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Sun, 21 Apr 2024 19:59:12 +0300 Subject: [PATCH] wallet: fix confirmations count, ability to repost and cancel transaction at posting state after timeout, repost height, fix posting state flag, cancel tx at send/receive modal --- src/gui/views/wallets/wallet/content.rs | 5 +- src/gui/views/wallets/wallet/messages.rs | 37 ++++++++++--- src/gui/views/wallets/wallet/txs.rs | 43 ++++++++++++--- src/wallet/types.rs | 17 +++--- src/wallet/wallet.rs | 68 ++++++++++++++++++------ 5 files changed, 132 insertions(+), 38 deletions(-) diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index 7fd7ae6..d92b6ea 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -358,8 +358,9 @@ impl WalletContent { columns[1].vertical_centered_justified(|ui| { let is_messages = current_type == WalletTabType::Messages; View::tab_button(ui, CHAT_CIRCLE_TEXT, is_messages, || { - let dandelion = wallet.get_config().use_dandelion.unwrap_or(true); - self.current_tab = Box::new(WalletMessages::new(dandelion)); + self.current_tab = Box::new( + WalletMessages::new(wallet.can_use_dandelion()) + ); }); }); columns[2].vertical_centered_justified(|ui| { diff --git a/src/gui/views/wallets/wallet/messages.rs b/src/gui/views/wallets/wallet/messages.rs index 80e9e42..1f7850d 100644 --- a/src/gui/views/wallets/wallet/messages.rs +++ b/src/gui/views/wallets/wallet/messages.rs @@ -671,7 +671,7 @@ impl WalletMessages { } else { t!("wallets.invoice_desc","amount" => amount_format) }; - ui.label(RichText::new(desc_text).size(16.0).color(Colors::INACTIVE_TEXT)); + ui.label(RichText::new(desc_text).size(16.0).color(Colors::GRAY)); ui.add_space(6.0); View::horizontal_line(ui, Colors::ITEM_STROKE); ui.add_space(3.0); @@ -699,12 +699,37 @@ impl WalletMessages { }); ui.add_space(2.0); View::horizontal_line(ui, Colors::ITEM_STROKE); - ui.add_space(10.0); + }); - // Draw copy button. - let copy_text = format!("{} {}", COPY, t!("copy")); - View::button(ui, copy_text, Colors::BUTTON, || { - cb.copy_string_to_buffer(self.request_edit.clone()); + // Show modal buttons. + ui.add_space(12.0); + // Setup spacing between buttons. + ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); + + ui.columns(2, |columns| { + columns[0].vertical_centered_justified(|ui| { + // Button to cancel transaction. + let clear_text = format!("{} {}", PROHIBIT, t!("modal.cancel")); + View::button(ui, clear_text, Colors::BUTTON, || { + if let Ok(slate) = wallet.parse_slatepack(self.request_edit.clone()) { + if let Some(tx) = wallet.tx_by_slate(&slate) { + wallet.cancel(tx.data.id); + } + } + self.amount_edit = "".to_string(); + self.request_edit = "".to_string(); + modal.close(); + }); + }); + columns[1].vertical_centered_justified(|ui| { + // Draw copy button. + let copy_text = format!("{} {}", COPY, t!("copy")); + View::button(ui, copy_text, Colors::BUTTON, || { + cb.copy_string_to_buffer(self.request_edit.clone()); + self.amount_edit = "".to_string(); + self.request_edit = "".to_string(); + modal.close(); + }); }); }); diff --git a/src/gui/views/wallets/wallet/txs.rs b/src/gui/views/wallets/wallet/txs.rs index 5c16c78..2158a3a 100644 --- a/src/gui/views/wallets/wallet/txs.rs +++ b/src/gui/views/wallets/wallet/txs.rs @@ -15,10 +15,10 @@ use egui::{Align, Id, Layout, Margin, RichText, Rounding, ScrollArea}; use egui::scroll_area::ScrollBarVisibility; use grin_core::core::amount_to_hr_string; -use grin_wallet_libwallet::{TxLogEntryType}; +use grin_wallet_libwallet::{Slate, SlateState, TxLogEntryType}; use crate::gui::Colors; -use crate::gui::icons::{ARROW_CIRCLE_DOWN, BRIDGE, CALENDAR_CHECK, CHAT_CIRCLE_TEXT, CHECK_CIRCLE, DOTS_THREE_CIRCLE, FILE_TEXT, GEAR_FINE, PROHIBIT, X_CIRCLE}; +use crate::gui::icons::{ARROW_CIRCLE_DOWN, ARROWS_CLOCKWISE, BRIDGE, CALENDAR_CHECK, CHAT_CIRCLE_TEXT, CHECK_CIRCLE, DOTS_THREE_CIRCLE, FILE_TEXT, GEAR_FINE, PROHIBIT, X_CIRCLE}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Root, View}; use crate::gui::views::wallets::types::WalletTab; @@ -183,14 +183,43 @@ fn tx_item_ui(ui: &mut egui::Ui, //TODO: Show tx info }); - if !tx.posting && !tx.data.confirmed && + // Setup flag to repost unconfirmed posting transaction after min confirmation time. + let last_height = data.info.last_confirmed_height; + let min_conf = data.info.minimum_confirmations; + let can_repost = tx.posting && tx.repost_height.is_some() && + last_height - tx.repost_height.unwrap() > min_conf; + + // Draw cancel button for txs to repost or also non-cancelled, non-posting. + if can_repost || (!tx.posting && !tx.data.confirmed && tx.data.tx_type != TxLogEntryType::TxReceivedCancelled - && tx.data.tx_type != TxLogEntryType::TxSentCancelled { + && tx.data.tx_type != TxLogEntryType::TxSentCancelled) { View::item_button(ui, Rounding::default(), PROHIBIT, Some(Colors::RED), || { wallet.cancel(tx.data.id); }); } + // Draw button to repost transaction. + if can_repost { + View::item_button(ui, + Rounding::default(), + ARROWS_CLOCKWISE, + Some(Colors::GREEN), || { + // Create slate to check existing file. + let mut slate = Slate::blank(1, false); + slate.id = tx.data.tx_slate_id.unwrap(); + slate.state = match tx.data.tx_type { + TxLogEntryType::TxReceived => SlateState::Invoice3, + _ => SlateState::Standard3 + }; + // Post tx after getting slate from slatepack file. + if let Some(sp) = wallet.read_slatepack(&slate) { + if let Ok(s) = wallet.parse_slatepack(sp) { + let _ = wallet.post(&s, wallet.can_use_dandelion()); + } + } + }); + } + let layout_size = ui.available_size(); ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { ui.add_space(12.0); @@ -230,9 +259,7 @@ fn tx_item_ui(ui: &mut egui::Ui, || tx.data.tx_type == TxLogEntryType::TxReceivedCancelled; if is_canceled { format!("{} {}", X_CIRCLE, t!("wallets.tx_canceled")) - } else if tx.posting || (tx.data.kernel_excess.is_some() && - (tx.data.tx_type == TxLogEntryType::TxReceived || - tx.data.tx_type == TxLogEntryType::TxSent)) { + } else if tx.posting { format!("{} {}", DOTS_THREE_CIRCLE, t!("wallets.tx_finalizing")) } else { match tx.data.tx_type { @@ -254,7 +281,7 @@ fn tx_item_ui(ui: &mut egui::Ui, format!("{} {}", CHECK_CIRCLE, t!("wallets.tx_confirmed")) }, TxLogEntryType::TxSent | TxLogEntryType::TxReceived => { - if data.info.last_confirmed_height - tx_height > min_conf + 1 { + if data.info.last_confirmed_height - tx_height > min_conf { let text = if tx.data.tx_type == TxLogEntryType::TxSent { t!("wallets.tx_sent") } else { diff --git a/src/wallet/types.rs b/src/wallet/types.rs index bca8408..6bc01bf 100644 --- a/src/wallet/types.rs +++ b/src/wallet/types.rs @@ -28,7 +28,7 @@ pub enum PhraseMode { Import } -/// Mnemonic phrase size based on words count. +/// Mnemonic phrase size based on entropy. #[derive(PartialEq, Clone)] pub enum PhraseSize { Words12, Words15, Words18, Words21, Words24 } @@ -41,7 +41,7 @@ impl PhraseSize { PhraseSize::Words24 ]; - /// Gen words count number. + /// Get entropy value. pub fn value(&self) -> usize { match *self { PhraseSize::Words12 => 12, @@ -52,7 +52,7 @@ impl PhraseSize { } } - /// Gen entropy size for current phrase size. + /// Get entropy size for current phrase size. pub fn entropy_size(&self) -> usize { match *self { PhraseSize::Words12 => 16, @@ -63,6 +63,7 @@ impl PhraseSize { } } + /// Get phrase type for entropy size. pub fn type_for_value(count: usize) -> Option { if Self::is_correct_count(count) { match count { @@ -90,7 +91,7 @@ impl PhraseSize { } } - /// Check if correct word count provided. + /// Check if correct entropy size was provided. pub fn is_correct_count(count: usize) -> bool { count == 12 || count == 15 || count == 18 || count == 21 || count == 24 } @@ -144,8 +145,10 @@ pub struct WalletData { pub struct WalletTransaction { /// Transaction information. pub data: TxLogEntry, - /// Calculated total transaction amount. + /// Calculated transaction amount between debited and credited amount. pub amount: u64, - /// Flag to check if transaction is posting after finalizing. - pub posting: bool + /// Flag to check if transaction is posting after finalization. + pub posting: bool, + /// Last wallet block height of transaction reposting. + pub repost_height: Option } \ No newline at end of file diff --git a/src/wallet/wallet.rs b/src/wallet/wallet.rs index cc25c57..a1e8a8d 100644 --- a/src/wallet/wallet.rs +++ b/src/wallet/wallet.rs @@ -13,7 +13,6 @@ // limitations under the License. use std::{fs, thread}; -use std::collections::BTreeSet; use std::fs::File; use std::io::Write; use std::net::{SocketAddr, TcpListener}; @@ -64,6 +63,7 @@ pub struct Wallet { is_open: Arc, /// Flag to check if wallet is closing. closing: Arc, + /// Flag to check if wallet was deleted to remove it from the list. deleted: Arc, @@ -222,7 +222,13 @@ impl Wallet { w_config.save(); } - /// Update usage of Dandelion to broadcast transactions. + /// Check if Dandelion usage is needed to post transactions. + pub fn can_use_dandelion(&self) -> bool { + let r_config = self.config.read().unwrap(); + r_config.use_dandelion.unwrap_or(false) + } + + /// Update usage of Dandelion to post transactions. pub fn update_use_dandelion(&self, use_dandelion: bool) { let mut w_config = self.config.write().unwrap(); w_config.use_dandelion = Some(use_dandelion); @@ -470,7 +476,7 @@ impl Wallet { } /// Create Slatepack message from provided slate. - fn create_slatepack_message(&self, slate: Slate) -> Result { + fn create_slatepack_message(&self, slate: &Slate) -> Result { let mut message = "".to_string(); let mut api = Owner::new(self.instance.clone().unwrap(), None); controller::owner_single_use(None, None, Some(&mut api), |api, m| { @@ -528,7 +534,7 @@ impl Wallet { api.tx_lock_outputs(None, &slate)?; // Create Slatepack message response. - let response = self.create_slatepack_message(slate)?; + let response = self.create_slatepack_message(&slate)?; // Sync wallet info. self.sync(); @@ -547,7 +553,7 @@ impl Wallet { let slate = api.issue_invoice_tx(None, args)?; // Create Slatepack message response. - let response = self.create_slatepack_message(slate)?; + let response = self.create_slatepack_message(&slate)?; // Sync wallet info. self.sync(); @@ -571,7 +577,7 @@ impl Wallet { api.tx_lock_outputs(None, &slate)?; // Create Slatepack message response. - let response = self.create_slatepack_message(slate)?; + let response = self.create_slatepack_message(&slate)?; // Sync wallet info. self.sync(); @@ -588,7 +594,7 @@ impl Wallet { Ok(()) })?; // Create Slatepack message response. - let response = self.create_slatepack_message(slate)?; + let response = self.create_slatepack_message(&slate)?; // Sync wallet info. self.sync(); @@ -601,10 +607,9 @@ impl Wallet { let mut slate = self.parse_slatepack(message)?; let api = Owner::new(self.instance.clone().unwrap(), None); slate = api.finalize_tx(None, &slate)?; - // Create Slatepack message. - let _ = self.create_slatepack_message(slate.clone())?; // Post transaction to blockchain. - api.post_tx(None, &slate, dandelion)?; + let _ = self.create_slatepack_message(&slate)?; + let _ = self.post(&slate, dandelion); // Sync wallet info. self.sync(); Ok(slate) @@ -615,6 +620,24 @@ impl Wallet { // Post transaction to blockchain. let api = Owner::new(self.instance.clone().unwrap(), None); api.post_tx(None, slate, dandelion)?; + // Setup transaction repost height and posting flag. + let mut slate = slate.clone(); + if slate.state == SlateState::Invoice2 { + slate.state = SlateState::Invoice3 + } else if slate.state == SlateState::Standard2 { + slate.state = SlateState::Standard3 + }; + if let Some(tx) = self.tx_by_slate(&slate) { + let mut w_data = self.data.write().unwrap(); + let mut data = w_data.clone().unwrap(); + for t in &mut data.txs { + if t.data.id == tx.data.id { + t.repost_height = Some(data.info.last_confirmed_height); + t.posting = true; + } + } + *w_data = Some(data); + } // Sync wallet info. self.sync(); Ok(()) @@ -922,7 +945,7 @@ fn sync_wallet_data(wallet: &Wallet) { }); let txs_args = RetrieveTxQueryArgs { - exclude_cancelled: Some(true), + exclude_cancelled: Some(false), sort_field: Some(RetrieveTxQuerySortField::CreationTimestamp), sort_order: Some(RetrieveTxQuerySortOrder::Desc), ..Default::default() @@ -956,9 +979,10 @@ fn sync_wallet_data(wallet: &Wallet) { }).collect::>(); // Create wallet txs. - let mut txs: Vec = vec![]; + let mut new_txs: Vec = vec![]; for tx in &filter_txs { println!("{}", serde_json::to_string(tx).unwrap()); + // Setup transaction amount. let amount = if tx.amount_debited > tx.amount_credited { tx.amount_debited - tx.amount_credited } else { @@ -979,7 +1003,7 @@ fn sync_wallet_data(wallet: &Wallet) { // Setup posting status if we have other tx with same slate id. let mut same_tx_posting = false; - for t in &mut txs { + for t in &mut new_txs { if t.data.tx_slate_id == tx.tx_slate_id && tx.tx_type != t.data.tx_type { same_tx_posting = t.posting || @@ -995,16 +1019,30 @@ fn sync_wallet_data(wallet: &Wallet) { false }; - txs.push(WalletTransaction { + // Setup reposting height. + let mut repost_height = None; + if posting { + if let Some(mut data) = wallet.get_data() { + for t in data.txs { + if t.data.id == tx.id { + repost_height = t.repost_height; + break; + } + } + } + } + + new_txs.push(WalletTransaction { data: tx.clone(), amount, posting, + repost_height, }) } // Update wallet data. let mut w_data = wallet.data.write().unwrap(); - *w_data = Some(WalletData { info: info.1, txs }); + *w_data = Some(WalletData { info: info.1, txs: new_txs }); return; } }