From cec168bf016173b964e3ac65abba2373c38575e3 Mon Sep 17 00:00:00 2001 From: Max <63856008+Maxnflaxl@users.noreply.github.com> Date: Fri, 19 Apr 2024 17:44:12 +0200 Subject: [PATCH 01/46] Create de.yml --- locales/de.yml | 239 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 locales/de.yml diff --git a/locales/de.yml b/locales/de.yml new file mode 100644 index 0000000..0f149b1 --- /dev/null +++ b/locales/de.yml @@ -0,0 +1,239 @@ +copy: Kopieren +paste: Einfügen +continue: Weiter +complete: Fertig +error: Error +retry: Erneut versuchen +close: Schließen +change: Ändern +show: Zeigen +delete: Löschen +clear: Clear +create: Erstellen +wallets: + await_conf_amount: Erwarte Bestätigung + await_fin_amount: Warten auf die Fertigstellung + locked_amount: Gesperrt + txs_empty: 'Um Geld manuell oder per Transport zu empfangen oder zu senden, verwenden Sie die Schaltflächen %{message} oder %{transport} unten auf dem Bildschirm. Um die Wallet-Einstellungen zu ändern, drücken Sie %{settings}.' + title: Wallets + create_desc: Erstellen oder importieren Sie ein bestehendes Wallet mit dem Seed-Phrase. + add: Wallet hinzufügen + name: 'Name:' + pass: 'Passwort:' + pass_empty: Wallet Passwort eingeben + current_pass: 'Aktuelles Passwort:' + new_pass: 'Neues Passwort:' + min_tx_conf_count: 'Mindestanzahl an Bestätigungen für Transaktionen:' + recover: Wiederherstellen + recovery_phrase: Wiederherstellungssatz + words_count: 'Wortanzahl:' + enter_word: 'Wort #%{number} eingeben:' + not_valid_word: Das eingegebene Wort ist ungültig + not_valid_phrase: Der eingegebene Satz ist ungültig + create_phrase_desc: Schreiben Sie Ihre Wiederherstellungsphrase sicher auf und speichern Sie sie. + restore_phrase_desc: Geben Sie Wörter aus Ihrer gespeicherten Wiederherstellungsphrase ein. + setup_conn_desc: Wählen Sie aus, wie Ihr Wallet eine Verbindung zum Netzwerk herstellt. + conn_method: Verbindungsmethode + ext_conn: 'Externe Verbindungen:' + add_node: Node hinzufügen + node_url: 'Node URL:' + node_secret: 'API Secret (optional):' + invalid_url: Die eingegebene URL ist ungültig + open: Wallet öffnen + wrong_pass: Das eingegebene Passwort ist falsch + locked: Gesperrt + unlocked: Entsperrt + enable_node: 'Aktivieren Sie die integrierte Node, um das Wallet zu verwenden, oder ändern Sie die Verbindungseinstellungen, indem Sie unten auf dem Bildschirm %{settings} auswählen.' + node_loading: 'Das Wallet wird nach der synchronisation der integrierten Node geladen. Sie können die Verbindungseinstellungen ändern, indem Sie unten auf dem Bildschirm %{settings} auswählen.' + loading: Wird geladen + closing: Schließen + checking: Überprüfung + default_wallet: Standard-Wallet + wallet_loading: Wallet wird geladen + wallet_closing: Wallet schließen + wallet_checking: Wallet prüfen + tx_loading: Laden von Transaktionen + default_account: Standardaccount + create_account: Account erstellen + accounts: Accounts + tx_sent: Gesendet + tx_received: Erhalten + tx_sending: Senden + tx_receiving: Erhalten + tx_confirming: Erwarte Bestätigung + tx_cancelling: Abbrechen + tx_canceled: Abgebrochen + tx_confirmed: Bestätigt + txs: Transaktionen + messages: Nachrichten + transport: Transport + input_slatepack_desc: 'Geben Sie eine Nachricht ein, um eine Antwort zu erstellen oder die Transaktion abzuschließen:' + send_slatepack_desc: 'Senden Sie eine Nachricht an den Empfänger, um die Transaktion abzuschließen:' + parse_slatepack_err: 'Bei der Verarbeitung der Nachricht ist ein Fehler aufgetreten. Überprüfen Sie die Eingabedaten:' + pay_balance_error: 'Der Kontostand reicht nicht aus, um %{amount} ツ und die Netzwerkgebühr zu bezahlen.' + parse_i1_slatepack_desc: 'Um %{amount} zu zahlen, senden Sie diese Nachricht an den Empfänger:' + parse_i2_slatepack_desc: 'Schließen Sie die Transaktion ab, um %{amount} ツ zu erhalten' + parse_s1_slatepack_desc: 'Um %{amount} zu erhalten, senden Sie diese Nachricht an den Absender:' + parse_s2_slatepack_desc: 'Schließen Sie die Transaktion ab, um %{amount} ツ zu senden' + response_slatepack_err: 'Beim Erstellen der Antwort ist ein Fehler aufgetreten. Überprüfen Sie die Eingabedaten:' + response_exists_err: 'Eine solche Transaktion existiert bereits:' + create_request_desc: 'Erstellen Sie eine Anfrage zum Senden oder Empfangen der Gelder:' + send_request_desc: 'Sie haben eine Anfrage zum Senden von %{amount} ツ erstellt. Senden Sie diese Nachricht an den Empfänger:' + send_slatepack_err: Beim Erstellen der Anfrage zum Senden von Geldern ist ein Fehler aufgetreten. Überprüfen Sie die Eingabedaten. + invoice_desc: 'Sie haben eine Anfrage zum Erhalt von %{amount} ツ erstellt. Senden Sie diese Nachricht an den Absender der Gelder:' + invoice_slatepack_err: Bei der Rechnungsstellung ist ein Fehler aufgetreten, überprüfen Sie die Eingabedaten. + finalize_slatepack_err: 'Bei der Finalisierung ist ein Fehler aufgetreten. Überprüfen Sie die Eingabedaten:' + finalize: Abschließen + use_dandelion: Dandelion verwenden + enter_amount_send: 'Sie haben %{amount} ツ. Geben Sie den zu sendenden Betrag ein:' + enter_amount_receive: 'Geben Sie den zu erhaltenden Betrag ein:' + recovery: Wiederherstellung + repair_wallet: Wallet reparieren + repair_desc: Überprüfen Sie ein Wallet und reparieren und stellen Sie bei Bedarf fehlende Ausgaben wieder her. Dieser Vorgang wird einige Zeit dauern. + repair_unavailable: Sie benötigen eine aktive Verbindung zum Knoten und eine abgeschlossene Wallet-Synchronisierung. + delete: Wallet löschen + delete_conf: Sind Sie sicher, dass Sie das Wallet löschen möchten? + delete_desc: Stellen Sie sicher, dass Sie Ihre Wiederherstellungsphrase gespeichert haben, um auf Gelder zugreifen zu können. + wallet_loading_err: 'Bei der Synchronisierung des Wallets ist ein Fehler aufgetreten. Sie können es erneut versuchen oder die Verbindungseinstellungen ändern, indem Sie unten auf dem Bildschirm %{settings} auswählen.' + wallet: Wallet + send: Senden + receive: Empfangen + settings: Wallet Einstellungen + change_server_confirmation: Um die Änderung der Verbindungseinstellungen zu übernehmen, müssen Sie Ihr Wallet erneut öffnen. Jetzt wieder öffnen? +network: + self: Netzwerk + type: 'Netzwerk Typ:' + connections: Verbindungen + node: Integrierte Node + metrics: Metriken + mining: Mining + settings: Node Einstellungen + enable_node: Node aktivieren + autorun: Autorun + disabled_server: 'Aktivieren Sie die integrierte Node oder fügen Sie eine weitere Verbindungsmethode hinzu, indem Sie oben links auf dem Bildschirm auf %{dots} drücken.' + no_ips: Auf Ihrem System sind keine IP-Adressen verfügbar. Der Server kann nicht gestartet werden. Überprüfen Sie Ihre Netzwerkkonnektivität. + available: Verfügbar + not_available: Nicht verfügbar + availability_check: Verfügbarkeitsprüfung +sync_status: + node_restarting: Node wird neu gestartet + node_down: Node ist ausgefallen + initial: Node startet + no_sync: Node läuft + awaiting_peers: Warten auf Peers + header_sync: Header werden heruntergeladen + header_sync_percent: 'Header werden heruntergeladen: %{percent}%' + tx_hashset_pibd: Downloadstatus (PIBD) + tx_hashset_pibd_percent: 'Downloadstatus (PIBD): %{percent}%' + tx_hashset_download: Downloadstatus + tx_hashset_download_percent: 'Downloadstatus: %{percent}%' + tx_hashset_setup_history: 'Vorbereitungsstatus (Verlauf): %{percent}%' + tx_hashset_setup_position: 'Vorbereitungsstatus (Position): %{percent}%' + tx_hashset_setup: Vorbereitungszustand + tx_hashset_range_proofs_validation: 'Validierungsstatus (range proofs): %{percent}%' + tx_hashset_kernels_validation: 'Validierungsstatus (Kernel): %{percent}%' + tx_hashset_save: Finalisierender Chainstatus + body_sync: Blöcke herunterladen + body_sync_percent: 'Blöcke herunterladen: %{percent}%' + shutdown: Node wird heruntergefahren +network_node: + header: Header + block: Block + hash: Hash + height: Höhe + difficulty: Schwierigkeit + time: Zeit + main_pool: Hauptpool + stem_pool: Stem-Pool + data: Daten + size: Größe (GB) + peers: Peers +network_metrics: + loading: Metriken werden nach der Synchronisierung verfügbar sein + emission: Emission + inflation: Inflation + supply: Supply + block_time: Blockzeit + reward: Belohnung + difficulty_window: 'Schwierigkeitsfenster %{size}' +network_mining: + loading: Mining wird nach der Synchronisierung verfügbar sein + info: 'Mining-Server aktiviert ist, können Sie seine Einstellungen ändern, indem Sie unten auf dem Bildschirm %{settings} wählen. Die Daten werden aktualisiert, wenn Geräte angeschlossen sind.' + restart_server_required: Ein Neustart des Servers ist erforderlich, um die Änderungen zu übernehmen. + rewards_wallet: Brieftasche für Belohnungen + server: Stratum Server + address: Addresse + miners: Miner + devices: Geräte + blocks_found: Gefundene Blöcke + hashrate: 'Hashrate (C%{bits})' + connected: Verbunden + disconnected: Getrennt +network_settings: + change_value: Wert ändern + stratum_ip: 'Stratum IP Addresse:' + stratum_port: 'Stratum Port:' + port_unavailable: Der angegebene Port ist nicht verfügbar + restart_node_required: Ein Neustart der Node ist erforderlich, um die Änderungen zu übernehmen. + enable: Aktivieren + disable: Deaktivieren + restart: Neustarten + server: Server + api_ip: 'API IP Addresse:' + api_port: 'API Port:' + api_secret: 'Rest API and V2 Owner API token:' + foreign_api_secret: 'Fremdes API token:' + disabled: Deaktiviert + enabled: Aktiviert + ftl: 'Das Future Time Limit (FTL):' + ftl_description: Begrenzung, wie weit in der Zukunft, relativ zur lokalen Zeit eines Knotens in Sekunden, der Zeitstempel eines neuen Blocks liegen darf, damit der Block akzeptiert wird. + not_valid_value: Eingegebener Wert ist nicht gültig + full_validation: Vollständige Validierung + full_validation_description: Ob bei der Verarbeitung jedes Blocks eine vollständige Kettenvalidierung durchgeführt werden soll (außer bei der Synchronisierung). + archive_mode: Archiv Modus + archive_mode_desc: Führen Sie den Knoten im vollständigen Archivmodus aus (für die Synchronisierung wird mehr Speicherplatz und Zeit benötigt). + attempt_time: 'Zeit des Miningsversuches (in Sekunden):' + attempt_time_desc: Die Zeitspanne, in der versucht wird, eine bestimmte Kopfzeile abzubauen, bevor der Abbau gestoppt und die Transaktionen erneut aus dem Pool gesammelt werden + min_share_diff: 'Der Mindestschwierigkeitsgrad des Shares:' + reset_settings_desc: Nodeeinstellungen auf Standardwerte zurücksetzen + reset_settings: Einstellungen zurücksetzen + reset: zurücksetzen + tx_pool: Transaktionspool + pool_fee: 'Grundgebühr, die in den Pool aufgenommen wird:' + reorg_period: 'Aufbewahrungsdauer des Reorg-Caches (in Minuten):' + max_tx_pool: 'Maximale Anzahl von Transaktionen im Pool:' + max_tx_stempool: 'Maximale Anzahl von Transaktionen im Stamm-Pool:' + max_tx_weight: 'Maximales Gesamtgewicht der Transaktionen, die zur Bildung eines Blocks ausgewählt werden können:' + epoch_duration: 'Epochendauer (in Sekunden):' + embargo_timer: 'Embargo-Timer (in Sekunden):' + aggregation_period: 'Aggregationszeitraum (in Sekunden):' + stem_probability: 'Wahrscheinlichkeit der Stem-Phase:' + stem_txs: Stem Transaktionen + p2p_server: P2P Server + p2p_port: 'P2P port:' + add_seed: DNS-Seed hinzufügen + seed_address: 'DNS Seed Addresse:' + add_peer: Peer hinzufügen + peer_address: 'Peer Addresse:' + peer_address_error: 'Geben Sie die IP-Adresse oder den DNS-Namen (stellen Sie sicher, dass der angegebene Host verfügbar ist) im richtigen Format ein, z. B: 192.168.0.1:1234 oder example.com:5678' + default: Standardeinstellung + allow_list: Erlaubt-Liste + allow_list_desc: Nur mit diesen Peers verbinden + deny_list: Ablehnungsliste + deny_list_desc: Niemals mit diesen Peers verbinden + favourites: Favoriten + favourites_desc: Eine Liste der bevorzugten Peers, mit denen eine Verbindung hergestellt werden soll. + ban_window: 'Wie viel Zeit (in Sekunden) ein gesperrter Peer gesperrt bleiben soll:' + ban_window_desc: Die Entscheidung über das Verbot trifft der Knoten auf der Grundlage der Korrektheit der von der Gegenstelle erhaltenen Daten. + max_inbound_count: 'Maximale Anzahl der eingehenden Peer-Verbindungen:' + max_outbound_count: 'Maximale Anzahl von ausgehenden Peer-Verbindungen:' + min_outbound_count: 'Mindestanzahl von ausgehenden Peer-Verbindungen:' + min_outbound_desc: Der Node-Server wird aktiv versuchen, weitere Peers hinzuzufügen, bis er mindestens diese Anzahl erreicht hat. +modal: + cancel: Abbrechen + save: Speichern + confirmation: Bestätigung + add: Hinzufügen +modal_exit: + description: Sind Sie sicher, dass Sie die Anwendung beenden wollen? + exit: Schließen -- 2.39.5 From d084fd6b593b7304659abb7acb57b76439bb17f7 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Sat, 20 Apr 2024 00:50:20 +0300 Subject: [PATCH 02/46] node: update stats less frequently, wait on restart --- src/node/node.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/node/node.rs b/src/node/node.rs index 2f3684a..b04cd3a 100644 --- a/src/node/node.rs +++ b/src/node/node.rs @@ -79,7 +79,7 @@ impl Default for Node { impl Node { /// Delay for thread to update the stats. - pub const STATS_UPDATE_DELAY: Duration = Duration::from_millis(250); + pub const STATS_UPDATE_DELAY: Duration = Duration::from_millis(1000); /// Stop the [`Server`] and setup exit flag after if needed. pub fn stop(exit_after_stop: bool) { @@ -212,15 +212,13 @@ impl Node { let mut first_start = true; loop { if Self::is_restarting() { - // Stop the server. server.stop(); - + thread::sleep(Duration::from_millis(5000)); // Reset stratum stats { let mut w_stratum_stats = NODE_STATE.stratum_stats.write(); *w_stratum_stats = StratumStats::default(); } - // Create new server. match start_node_server() { Ok(s) => { -- 2.39.5 From ab538d52ba2b3816122ba27de606e79c1de15a65 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Sat, 20 Apr 2024 14:25:03 +0300 Subject: [PATCH 03/46] metrics: fix list width --- src/gui/views/network/metrics.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/views/network/metrics.rs b/src/gui/views/network/metrics.rs index ae9d864..f184ae8 100644 --- a/src/gui/views/network/metrics.rs +++ b/src/gui/views/network/metrics.rs @@ -85,7 +85,7 @@ impl NetworkTab for NetworkMetrics { [false, true, false, true]); }); }); - ui.add_space(4.0); + ui.add_space(5.0); // Show difficulty adjustment window info. let difficulty_title = t!( @@ -151,6 +151,7 @@ fn block_item_ui(ui: &mut egui::Ui, db: &DiffBlock, rounding: Rounding) { ui.vertical(|ui| { // Draw round background. rect.min += vec2(8.0, 0.0); + rect.max -= vec2(8.0, 0.0); ui.painter().rect(rect, rounding, Colors::WHITE, View::ITEM_STROKE); ui.add_space(2.0); @@ -187,6 +188,7 @@ fn block_item_ui(ui: &mut egui::Ui, db: &DiffBlock, rounding: Rounding) { ui.add_space(2.0); }); + ui.add_space(8.0); }); }); } \ No newline at end of file -- 2.39.5 From 95be986e0ff1c4fad6dcc4adbec6089998c58149 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Sat, 20 Apr 2024 16:59:54 +0300 Subject: [PATCH 04/46] wallet + ui: separate transaction struct, fix tx cancel, fix tx cancel at finalizing state, post tx from slatepack --- locales/en.yml | 4 +- locales/ru.yml | 4 +- src/gui/views/network/node.rs | 8 +- src/gui/views/views.rs | 2 +- src/gui/views/wallets/wallet/content.rs | 10 +- src/gui/views/wallets/wallet/messages.rs | 237 +++++++++++-------- src/gui/views/wallets/wallet/txs.rs | 118 +++++----- src/wallet/config.rs | 17 +- src/wallet/types.rs | 13 +- src/wallet/wallet.rs | 278 +++++++++++++---------- 10 files changed, 395 insertions(+), 296 deletions(-) diff --git a/locales/en.yml b/locales/en.yml index e28cb13..b525f7b 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -61,8 +61,8 @@ wallets: tx_sending: Sending tx_receiving: Receiving tx_confirming: Awaiting confirmation - tx_cancelling: Cancelling tx_canceled: Canceled + tx_finalizing: Finalizing tx_confirmed: Confirmed txs: Transactions messages: Messages @@ -73,8 +73,10 @@ wallets: pay_balance_error: 'Account balance is insufficient to pay %{amount} ツ and network fee.' parse_i1_slatepack_desc: 'To pay %{amount} ツ send this message to the receiver:' parse_i2_slatepack_desc: 'Finalize transaction to receive %{amount} ツ' + parse_i3_slatepack_desc: 'Post transaction to finalize receiving of %{amount} ツ' parse_s1_slatepack_desc: 'To receive %{amount} ツ send this message to the sender:' parse_s2_slatepack_desc: 'Finalize transaction to send %{amount} ツ' + parse_s3_slatepack_desc: 'Post transaction to finalize sending of %{amount} ツ' response_slatepack_err: 'An error occurred during creation of the response, check input data:' response_exists_err: 'Such transaction already exists:' create_request_desc: 'Create request to send or receive the funds:' diff --git a/locales/ru.yml b/locales/ru.yml index b06939d..69fb776 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -61,8 +61,8 @@ wallets: tx_sending: Отправка tx_receiving: Получение tx_confirming: Ожидает подтверждения - tx_cancelling: Отмена tx_canceled: Отменено + tx_finalizing: Завершение tx_confirmed: Подтверждено txs: Транзакции messages: Сообщения @@ -73,8 +73,10 @@ wallets: pay_balance_error: 'Средств на аккаунте недостаточно для оплаты %{amount} ツ и комиссии сети.' parse_i1_slatepack_desc: 'Для оплаты %{amount} ツ отправьте это сообщение получателю:' parse_i2_slatepack_desc: 'Завершите транзакцию для получения %{amount} ツ' + parse_i3_slatepack_desc: 'Опубликуйте транзакцию для завершения получения %{amount} ツ' parse_s1_slatepack_desc: 'Для получения %{amount} ツ отправьте это сообщение отправителю:' parse_s2_slatepack_desc: 'Завершите транзакцию для отправки %{amount} ツ' + parse_s3_slatepack_desc: 'Опубликуйте транзакцию для завершения отправки %{amount} ツ' response_slatepack_err: 'Во время создания ответа произошла ошибка, проверьте входные данные:' response_exists_err: 'Такая транзакция уже существует:' create_request_desc: 'Cоздать запрос на получение или отправку средств:' diff --git a/src/gui/views/network/node.rs b/src/gui/views/network/node.rs index edd0d0c..e99d929 100644 --- a/src/gui/views/network/node.rs +++ b/src/gui/views/network/node.rs @@ -51,7 +51,7 @@ impl NetworkTab for NetworkNode { .id_source("integrated_node") .auto_shrink([false; 2]) .show(ui, |ui| { - ui.add_space(1.0); + ui.add_space(2.0); // Show header info. View::sub_title(ui, format!("{} {}", FLOW_ARROW, t!("network_node.header"))); @@ -85,7 +85,7 @@ impl NetworkTab for NetworkNode { [false, false, false, true]); }); }); - ui.add_space(4.0); + ui.add_space(5.0); // Show block info. View::sub_title(ui, format!("{} {}", CUBE, t!("network_node.block"))); @@ -119,7 +119,7 @@ impl NetworkTab for NetworkNode { [false, false, false, true]); }); }); - ui.add_space(4.0); + ui.add_space(5.0); // Show data info. View::sub_title(ui, format!("{} {}", SHARE_NETWORK, t!("network_node.data"))); @@ -161,7 +161,7 @@ impl NetworkTab for NetworkNode { [false, false, false, true]); }); }); - ui.add_space(4.0); + ui.add_space(5.0); // Show peer stats when available. if stats.peer_count > 0 { diff --git a/src/gui/views/views.rs b/src/gui/views/views.rs index a1f0f6f..98c0bcc 100644 --- a/src/gui/views/views.rs +++ b/src/gui/views/views.rs @@ -544,7 +544,7 @@ impl View { pub fn format_time(ts: i64) -> String { let utc_offset = chrono::Local::now().offset().local_minus_utc(); let utc_time = ts + utc_offset as i64; - let tx_time = chrono::NaiveDateTime::from_timestamp_opt(utc_time, 0).unwrap(); + let tx_time = chrono::DateTime::from_timestamp(utc_time, 0).unwrap(); tx_time.format("%d/%m/%Y %H:%M:%S").to_string() } diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index b4110eb..7fd7ae6 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -118,7 +118,7 @@ impl WalletContent { ui.vertical_centered(|ui| { // Draw wallet tabs. View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { - self.tabs_ui(ui); + self.tabs_ui(ui, wallet); }); }); }); @@ -340,7 +340,7 @@ impl WalletContent { } /// Draw tab buttons in the bottom of the screen. - fn tabs_ui(&mut self, ui: &mut egui::Ui) { + fn tabs_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet) { ui.scope(|ui| { // Setup spacing between tabs. ui.style_mut().spacing.item_spacing = egui::vec2(4.0, 0.0); @@ -356,8 +356,10 @@ impl WalletContent { }); }); columns[1].vertical_centered_justified(|ui| { - View::tab_button(ui, CHAT_CIRCLE_TEXT, current_type == WalletTabType::Messages, || { - self.current_tab = Box::new(WalletMessages::default()); + 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)); }); }); 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 b1c065a..98302d0 100644 --- a/src/gui/views/wallets/wallet/messages.rs +++ b/src/gui/views/wallets/wallet/messages.rs @@ -15,16 +15,17 @@ use egui::{Id, Margin, RichText, ScrollArea}; use egui::scroll_area::ScrollBarVisibility; use grin_core::core::{amount_from_hr_string, amount_to_hr_string}; -use grin_wallet_libwallet::{Slate, SlateState, TxLogEntry}; +use grin_wallet_libwallet::{Slate, SlateState}; use log::error; use crate::gui::Colors; -use crate::gui::icons::{BROOM, CLIPBOARD_TEXT, COPY, DOWNLOAD, UPLOAD}; +use crate::gui::icons::{BROOM, CLIPBOARD_TEXT, COPY, DOWNLOAD, PROHIBIT, UPLOAD}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, Root, View}; use crate::gui::views::types::{ModalPosition, TextEditOptions}; use crate::gui::views::wallets::wallet::types::{SLATEPACK_MESSAGE_HINT, WalletTab, WalletTabType}; use crate::gui::views::wallets::wallet::WalletContent; +use crate::wallet::types::WalletTransaction; use crate::wallet::Wallet; #[derive(Clone, Eq, PartialEq, Debug, thiserror::Error)] @@ -64,7 +65,7 @@ pub struct WalletMessages { /// Generated Slatepack response message. response_edit: String, /// Flag to check if Dandelion is needed to finalize transaction. - use_dandelion: Option, + dandelion: bool, /// Amount to send or receive. amount_edit: String, @@ -77,15 +78,15 @@ pub struct WalletMessages { /// Identifier for invoice amount [`Modal`]. const AMOUNT_MODAL: &'static str = "amount_modal"; -impl Default for WalletMessages { - fn default() -> Self { +impl WalletMessages { + pub fn new(dandelion: bool) -> Self { Self { send_request: false, message_edit: "".to_string(), message_slate: None, message_error: None, response_edit: "".to_string(), - use_dandelion: None, + dandelion, amount_edit: "".to_string(), request_edit: "".to_string(), request_error: None, @@ -202,12 +203,18 @@ impl WalletMessages { SlateState::Standard2 => { t!("wallets.parse_s2_slatepack_desc","amount" => amount) } + SlateState::Standard3 => { + t!("wallets.parse_s3_slatepack_desc","amount" => amount) + } SlateState::Invoice1 => { t!("wallets.parse_i1_slatepack_desc","amount" => amount) } SlateState::Invoice2 => { t!("wallets.parse_i2_slatepack_desc","amount" => amount) } + SlateState::Invoice3 => { + t!("wallets.parse_i3_slatepack_desc","amount" => amount) + } _ => { t!("wallets.input_slatepack_desc") } @@ -215,10 +222,10 @@ impl WalletMessages { }; ui.label(RichText::new(desc_text).size(16.0).color(Colors::INACTIVE_TEXT)); } - ui.add_space(7.0); + ui.add_space(6.0); // Setup Slatepack message text input. - let mut message = if response_empty { + let message = if response_empty { &mut self.message_edit } else { &mut self.response_edit @@ -253,22 +260,52 @@ impl WalletMessages { View::horizontal_line(ui, Colors::ITEM_STROKE); ui.add_space(10.0); + // Parse Slatepack message if input field was changed, resetting message error. + if &message_before != message { + self.parse_message(wallet); + } + // Draw buttons to clear/copy/paste. let fields_empty = self.message_edit.is_empty() && self.response_edit.is_empty(); let columns_num = if fields_empty { 1 } else { 2 }; let mut show_dandelion = false; ui.scope(|ui| { // Setup spacing between buttons. - ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0); + ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); ui.columns(columns_num, |columns| { let first_column_content = |ui: &mut egui::Ui| { - if self.message_slate.is_some() && self.message_error.is_none() { - self.clear_message_button_ui(ui); + if self.message_slate.is_some() { + if self.response_edit.is_empty() { + let clear_text = format!("{} {}", BROOM, t!("clear")); + View::button(ui, clear_text, Colors::BUTTON, || { + self.message_edit.clear(); + self.response_edit.clear(); + self.message_error = None; + self.message_slate = None; + }); + } else { + let clear_text = format!("{} {}", PROHIBIT, t!("modal.cancel")); + View::button(ui, clear_text, Colors::BUTTON, || { + let slate = self.message_slate.clone().unwrap(); + if let Some(tx) = wallet.tx_by_slate(&slate) { + wallet.cancel(tx.data.id); + self.message_edit.clear(); + self.response_edit.clear(); + self.message_slate = None; + } + }); + } } else { let paste_text = format!("{} {}", CLIPBOARD_TEXT, t!("paste")); View::button(ui, paste_text, Colors::BUTTON, || { - self.message_edit = cb.get_string_from_buffer(); + let buf = cb.get_string_from_buffer(); + let previous = self.message_edit.clone(); + self.message_edit = buf.clone(); + // Parse Slatepack message resetting message error. + if buf != previous { + self.parse_message(wallet); + } }); } }; @@ -277,27 +314,53 @@ impl WalletMessages { } else { columns[0].vertical_centered_justified(first_column_content); columns[1].vertical_centered_justified(|ui| { - if self.message_error.is_some() { - self.clear_message_button_ui(ui); - } else if !self.response_edit.is_empty() { - let copy_text = format!("{} {}", COPY, t!("copy")); - View::button(ui, copy_text, Colors::BUTTON, || { - cb.copy_string_to_buffer(self.response_edit.clone()); - }); - } else { - show_dandelion = true; - View::button(ui, t!("wallets.finalize"), Colors::GOLD, || { - let message = self.message_edit.clone(); - let use_dandelion = self.use_dandelion.unwrap(); - if let Ok(_) = wallet.finalize(message, use_dandelion) { + if self.message_slate.is_some() { + if !self.response_edit.is_empty() { + let copy_text = format!("{} {}", COPY, t!("copy")); + View::button(ui, copy_text, Colors::BUTTON, || { + cb.copy_string_to_buffer(self.response_edit.clone()); self.message_edit.clear(); + self.response_edit.clear(); self.message_slate = None; - - } else { - self.message_error = Some( - MessageError::Finalize(t!("wallets.finalize_slatepack_err")) - ); - } + }); + } else { + show_dandelion = true; + View::button(ui, t!("wallets.finalize"), Colors::GOLD, || { + let message = self.message_edit.clone(); + let slate = self.message_slate.clone().unwrap(); + if slate.state == SlateState::Invoice3 || + slate.state == SlateState::Standard3 { + if let Ok(_) = wallet.post(&slate, self.dandelion) { + self.message_edit.clear(); + self.message_slate = None; + } else { + self.message_error = Some( + MessageError::Finalize( + t!("wallets.finalize_slatepack_err") + ) + ); + } + } else { + if let Ok(_) = wallet.finalize(message, self.dandelion) { + self.message_edit.clear(); + self.message_slate = None; + } else { + self.message_error = Some( + MessageError::Finalize( + t!("wallets.finalize_slatepack_err") + ) + ); + } + } + }); + } + } else { + let clear_text = format!("{} {}", BROOM, t!("clear")); + View::button(ui, clear_text, Colors::BUTTON, || { + self.message_error = None; + self.message_edit.clear(); + self.response_edit.clear(); + self.message_slate = None; }); } }); @@ -307,35 +370,17 @@ impl WalletMessages { // Draw setup of ability to post transaction with Dandelion. if show_dandelion { - if self.use_dandelion.is_none() { - self.use_dandelion = if let Some(u) = wallet.get_config().use_dandelion { - Some(u) - } else { - Some(true) - }; - } - let use_dandelion = self.use_dandelion.unwrap(); - View::checkbox(ui, use_dandelion, t!("wallets.use_dandelion"), || { - self.use_dandelion = Some(!use_dandelion); - wallet.update_use_dandelion(use_dandelion); + let dandelion_before = self.dandelion; + View::checkbox(ui, dandelion_before, t!("wallets.use_dandelion"), || { + self.dandelion = !dandelion_before; + wallet.update_use_dandelion(self.dandelion); }); } - - message = if response_empty { - &mut self.message_edit - } else { - &mut self.response_edit - }; - - // Parse Slatepack message if input field was changed, resetting message error. - if &message_before != message { - self.message_error = None; - self.parse_message(wallet); - } } - /// Parse message input into [`Slate`], making operations like receive or pay to confirm. + /// Parse message input into [`Slate`] updating slate and response input. fn parse_message(&mut self, wallet: &mut Wallet) { + self.message_error = None; if self.message_edit.is_empty() { return; } @@ -348,16 +393,7 @@ impl WalletMessages { self.response_edit = resp; } else { // Check if tx with same slate id already exists. - let mut exists_tx = false; - let _ = wallet.get_data().unwrap().txs.clone().iter().map(|tx| { - if tx.tx_slate_id == Some(slate.id) { - exists_tx= true; - self.message_error = Some( - MessageError::Response(t!("wallets.response_exists_err")) - ); - } - tx - }).collect::>(); + let exists_tx = wallet.tx_by_slate(&slate).is_some(); if exists_tx { return; } @@ -389,24 +425,21 @@ impl WalletMessages { } } } - _ => {} + _ => { + self.response_edit = "".to_string(); + } } // Try to get amount from transaction by id. if slate.amount == 0 { let _ = wallet.get_data().unwrap().txs.clone().iter().map(|tx| { - if tx.tx_slate_id == Some(slate.id) { + if tx.data.tx_slate_id == Some(slate.id) { if slate.amount == 0 { - let amount = if tx.amount_debited > tx.amount_credited { - tx.amount_debited - tx.amount_credited - } else { - tx.amount_credited - tx.amount_debited - }; - slate.amount = amount; + slate.amount = tx.amount; } } tx - }).collect::>(); + }).collect::>(); } self.message_slate = Some(slate.clone()); } else { @@ -415,17 +448,6 @@ impl WalletMessages { } } - /// Draw button to clear entered message, slate and errors. - fn clear_message_button_ui(&mut self, ui: &mut egui::Ui) { - let clear_text = format!("{} {}", BROOM, t!("clear")); - View::button(ui, clear_text, Colors::BUTTON, || { - self.message_error = None; - self.message_edit.clear(); - self.response_edit.clear(); - self.message_slate = None; - }); - } - /// Draw creation of request to send or receive funds. fn request_ui(&mut self, ui: &mut egui::Ui, @@ -436,7 +458,7 @@ impl WalletMessages { ui.add_space(7.0); // Setup spacing between buttons. - ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0); + ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); ui.columns(2, |columns| { columns[0].vertical_centered_justified(|ui| { @@ -499,27 +521,42 @@ impl WalletMessages { // Draw invoice amount text edit. let amount_edit_id = Id::from(modal.id).with(wallet.get_config().id); let amount_edit_opts = TextEditOptions::new(amount_edit_id).h_center(); - let mut amount_edit_before = self.amount_edit.clone(); - View::text_edit(ui, cb, &mut amount_edit_before, amount_edit_opts); + let amount_edit_before = self.amount_edit.clone(); + View::text_edit(ui, cb, &mut self.amount_edit, amount_edit_opts); // Check value if input was changed. if amount_edit_before != self.amount_edit { self.request_error = None; - match amount_from_hr_string(amount_edit_before.as_str()) { - Ok(a) => { - if a <= 0 { - return; - } - // Do not input amount more than balance in sending. - if self.send_request { - let b = wallet.get_data().unwrap().info.amount_currently_spendable; - if b < a { - return; + if !self.amount_edit.is_empty() { + match amount_from_hr_string(self.amount_edit.as_str()) { + Ok(a) => { + if !self.amount_edit.contains(".") { + // To avoid input of several "0". + if a == 0 { + self.amount_edit = "0".to_string(); + return; + } + } else { + // Check input after ".". + let parts = self.amount_edit.split(".").collect::>(); + if parts.len() == 2 && parts[1].len() > 9 { + self.amount_edit = amount_edit_before; + return; + } + } + + // Do not input amount more than balance in sending. + if self.send_request { + let b = wallet.get_data().unwrap().info.amount_currently_spendable; + if b < a { + self.amount_edit = amount_edit_before; + } } } - self.amount_edit = amount_edit_before; + Err(_) => { + self.amount_edit = amount_edit_before; + } } - Err(_) => {} } } diff --git a/src/gui/views/wallets/wallet/txs.rs b/src/gui/views/wallets/wallet/txs.rs index 03f6fc5..93128cf 100644 --- a/src/gui/views/wallets/wallet/txs.rs +++ b/src/gui/views/wallets/wallet/txs.rs @@ -15,17 +15,19 @@ 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::{TxLogEntry, TxLogEntryType}; +use grin_wallet_libwallet::{TxLogEntryType}; use crate::gui::Colors; -use crate::gui::icons::{ARROW_CIRCLE_DOWN, ARROW_CIRCLE_UP, 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, 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; -use crate::gui::views::wallets::wallet::types::WalletTabType; +use crate::gui::views::wallets::wallet::types::{GRIN, WalletTabType}; use crate::gui::views::wallets::wallet::WalletContent; +use crate::wallet::types::{WalletData, WalletTransaction}; use crate::wallet::Wallet; + /// Wallet info tab content. #[derive(Default)] pub struct WalletInfo; @@ -69,6 +71,7 @@ impl WalletInfo { /// Draw transactions content. fn txs_ui(&self, ui: &mut egui::Ui, wallet: &mut Wallet) { let data = wallet.get_data().unwrap(); + let config = wallet.get_config(); let txs_size = data.txs.len(); // Show transactions info. @@ -133,7 +136,7 @@ impl WalletInfo { ui.add_space(3.0); ScrollArea::vertical() .scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible) - .id_source(Id::from("txs_content").with(wallet.get_config().id)) + .id_source(Id::from("txs_content").with(config.id)) .auto_shrink([false; 2]) .show_rows(ui, TX_ITEM_HEIGHT, txs_size, |ui, row_range| { ui.add_space(4.0); @@ -143,17 +146,11 @@ impl WalletInfo { // Setup item rounding. let item_rounding = View::item_rounding(index, txs_size, false); // Show transaction item. - tx_item_ui(ui, tx, item_rounding, data.info.last_confirmed_height, wallet); + tx_item_ui(ui, tx, item_rounding, config.min_confirmations, &data, wallet); } }); ui.add_space(2.0); }); - - // for tx in &data.txs { - // if tx.tx_type != TxLogEntryType::TxReceivedCancelled && tx.tx_type != TxLogEntryType::TxSentCancelled { - // println!("tx: {}", serde_json::to_string::(tx).unwrap()); - // } - // } } } @@ -162,9 +159,10 @@ const TX_ITEM_HEIGHT: f32 = 76.0; /// Draw transaction item. fn tx_item_ui(ui: &mut egui::Ui, - tx: &TxLogEntry, + tx: &WalletTransaction, mut rounding: Rounding, - last_height: u64, + min_conf: u64, + data: &WalletData, wallet: &mut Wallet) { // Setup layout size. let mut rect = ui.available_rect_before_wrap(); @@ -175,11 +173,6 @@ fn tx_item_ui(ui: &mut egui::Ui, let bg_rect = rect.clone(); ui.painter().rect(bg_rect, rounding, Colors::BUTTON, View::ITEM_STROKE); - // Setup transaction flags. - let is_canceled = tx.tx_type == TxLogEntryType::TxSentCancelled - || tx.tx_type == TxLogEntryType::TxReceivedCancelled; - let is_cancelling = wallet.is_cancelling(&tx.id); - ui.vertical(|ui| { ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { ui.add_space(-6.0); @@ -190,10 +183,11 @@ fn tx_item_ui(ui: &mut egui::Ui, //TODO: Show tx info }); - if !is_cancelling && !tx.confirmed && tx.tx_type != TxLogEntryType::TxReceivedCancelled - && tx.tx_type != TxLogEntryType::TxSentCancelled { + if !tx.posting && !tx.data.confirmed && + tx.data.tx_type != TxLogEntryType::TxReceivedCancelled + && tx.data.tx_type != TxLogEntryType::TxSentCancelled { View::item_button(ui, Rounding::default(), PROHIBIT, Some(Colors::RED), || { - wallet.cancel(tx.id); + wallet.cancel(tx.data.id); }); } @@ -201,18 +195,25 @@ fn tx_item_ui(ui: &mut egui::Ui, ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { ui.add_space(12.0); ui.vertical(|ui| { - // Setup transaction amount. ui.add_space(3.0); - let amount_text = if tx.amount_credited > tx.amount_debited { - format!("+{}", - amount_to_hr_string(tx.amount_credited - tx.amount_debited, true)) + + // Setup transaction amount. + let mut amount_text = if tx.data.tx_type == TxLogEntryType::TxSent || + tx.data.tx_type == TxLogEntryType::TxSentCancelled { + "-" + } else if tx.data.tx_type == TxLogEntryType::TxReceived || + tx.data.tx_type == TxLogEntryType::TxReceivedCancelled { + "+" } else { - format!("-{}", - amount_to_hr_string(tx.amount_debited - tx.amount_credited, true)) - }; + "" + }.to_string(); + amount_text = format!("{}{} {}", + amount_text, + amount_to_hr_string(tx.amount, true), + GRIN); // Setup amount color. - let amount_color = match tx.tx_type { + let amount_color = match tx.data.tx_type { TxLogEntryType::ConfirmedCoinbase => Colors::BLACK, TxLogEntryType::TxReceived => Colors::BLACK, TxLogEntryType::TxSent => Colors::BLACK, @@ -224,13 +225,16 @@ fn tx_item_ui(ui: &mut egui::Ui, ui.add_space(-2.0); // Setup transaction status text. - let status_text = if !tx.confirmed { - if wallet.is_cancelling(&tx.id) { - format!("{} {}", DOTS_THREE_CIRCLE, t!("wallets.tx_cancelling")) - } else if is_canceled { + let status_text = if !tx.data.confirmed { + let is_canceled = tx.data.tx_type == TxLogEntryType::TxSentCancelled + || tx.data.tx_type == TxLogEntryType::TxReceivedCancelled; + if is_canceled { format!("{} {}", X_CIRCLE, t!("wallets.tx_canceled")) + } else if tx.data.kernel_excess.is_some() && + tx.data.tx_type == TxLogEntryType::TxReceived { + format!("{} {}", DOTS_THREE_CIRCLE, t!("wallets.tx_finalizing")) } else { - match tx.tx_type { + match tx.data.tx_type { TxLogEntryType::TxReceived => { format!("{} {}", DOTS_THREE_CIRCLE, t!("wallets.tx_receiving")) }, @@ -238,31 +242,37 @@ fn tx_item_ui(ui: &mut egui::Ui, format!("{} {}", DOTS_THREE_CIRCLE, t!("wallets.tx_sending")) }, _ => { - format!("{} {}", DOTS_THREE_CIRCLE, t!("wallets.tx_confirming")) + format!("{} {}", DOTS_THREE_CIRCLE, t!("wallets.tx_confirmed")) } } } } else { - let tx_height = tx.kernel_lookup_min_height.unwrap_or(0); - let min_confirmations = wallet.get_config().min_confirmations; - match tx.tx_type { + let tx_height = tx.data.kernel_lookup_min_height.unwrap_or(0); + match tx.data.tx_type { TxLogEntryType::ConfirmedCoinbase => { format!("{} {}", CHECK_CIRCLE, t!("wallets.tx_confirmed")) }, - TxLogEntryType::TxReceived => { - if last_height - tx_height > min_confirmations { - format!("{} {}", ARROW_CIRCLE_DOWN, t!("wallets.tx_received")) + TxLogEntryType::TxSent | TxLogEntryType::TxReceived => { + if data.info.last_confirmed_height - tx_height > min_conf { + let text = if tx.data.tx_type == TxLogEntryType::TxSent { + t!("wallets.tx_sent") + } else { + t!("wallets.tx_received") + }; + format!("{} {}", ARROW_CIRCLE_DOWN, text) } else { - format!("{} {}", + let h = data.info.last_confirmed_height; + let left_conf = h - tx_height; + let conf_info = if h >= tx_height && left_conf <= min_conf { + format!("{}/{}", left_conf, min_conf) + } else { + "".to_string() + }; + format!("{} {} {}", DOTS_THREE_CIRCLE, - t!("wallets.tx_confirming")) - } - }, - TxLogEntryType::TxSent => { - if last_height - tx_height > min_confirmations { - format!("{} {}", ARROW_CIRCLE_UP, t!("wallets.tx_sent")) - } else { - format!("{} {}", DOTS_THREE_CIRCLE, t!("wallets.tx_confirming")) + t!("wallets.tx_confirming"), + conf_info + ) } }, _ => format!("{} {}", X_CIRCLE, t!("wallets.canceled")) @@ -270,14 +280,14 @@ fn tx_item_ui(ui: &mut egui::Ui, }; // Setup status text color. - let status_color = match tx.tx_type { + let status_color = match tx.data.tx_type { TxLogEntryType::ConfirmedCoinbase => Colors::TEXT, - TxLogEntryType::TxReceived => if tx.confirmed { + TxLogEntryType::TxReceived => if tx.data.confirmed { Colors::GREEN } else { Colors::TEXT }, - TxLogEntryType::TxSent => if tx.confirmed { + TxLogEntryType::TxSent => if tx.data.confirmed { Colors::RED } else { Colors::TEXT @@ -289,7 +299,7 @@ fn tx_item_ui(ui: &mut egui::Ui, ui.label(RichText::new(status_text).size(15.0).color(status_color)); // Setup transaction time. - let tx_time = View::format_time(tx.creation_ts.timestamp()); + let tx_time = View::format_time(tx.data.creation_ts.timestamp()); let tx_time_text = format!("{} {}", CALENDAR_CHECK, tx_time); ui.label(RichText::new(tx_time_text).size(15.0).color(Colors::GRAY)); }); diff --git a/src/wallet/config.rs b/src/wallet/config.rs index a1c9b05..ce1d80f 100644 --- a/src/wallet/config.rs +++ b/src/wallet/config.rs @@ -17,6 +17,7 @@ use std::path::PathBuf; use std::string::ToString; use grin_core::global::ChainTypes; +use grin_wallet_libwallet::{SlateState}; use serde_derive::{Deserialize, Serialize}; use crate::{AppConfig, Settings}; @@ -120,14 +121,16 @@ impl WalletConfig { config_path.to_str().unwrap().to_string() } - /// Get slatepacks data path for current wallet. - pub fn get_slatepacks_path(&self) -> PathBuf { - let mut slatepacks_dir = PathBuf::from(self.get_data_path()); - slatepacks_dir.push(SLATEPACKS_DIR_NAME); - if !slatepacks_dir.exists() { - let _ = fs::create_dir_all(slatepacks_dir.clone()); + /// Get Slatepacks data path for current wallet. + pub fn get_slatepack_path(&self, id: String, state: &SlateState) -> PathBuf { + let mut slatepack_dir = PathBuf::from(self.get_data_path()); + slatepack_dir.push(SLATEPACKS_DIR_NAME); + if !slatepack_dir.exists() { + let _ = fs::create_dir_all(slatepack_dir.clone()); } - slatepacks_dir + let slatepack_file_name = format!("{}.{}.slatepack", id, state); + slatepack_dir.push(slatepack_file_name); + slatepack_dir } /// Save wallet config. diff --git a/src/wallet/types.rs b/src/wallet/types.rs index c263494..bca8408 100644 --- a/src/wallet/types.rs +++ b/src/wallet/types.rs @@ -136,5 +136,16 @@ pub struct WalletData { /// Balance data for current account. pub info: WalletInfo, /// Transactions data. - pub txs: Vec + pub txs: Vec +} + +/// Wallet transaction data. +#[derive(Clone)] +pub struct WalletTransaction { + /// Transaction information. + pub data: TxLogEntry, + /// Calculated total transaction amount. + pub amount: u64, + /// Flag to check if transaction is posting after finalizing. + pub posting: bool } \ No newline at end of file diff --git a/src/wallet/wallet.rs b/src/wallet/wallet.rs index a39f40c..25d5aec 100644 --- a/src/wallet/wallet.rs +++ b/src/wallet/wallet.rs @@ -31,17 +31,16 @@ use grin_keychain::{ExtKeychain, Identifier, Keychain}; use grin_util::Mutex; use grin_util::types::ZeroingString; use grin_wallet_api::Owner; -use grin_wallet_controller::command::parse_slatepack; use grin_wallet_controller::controller; use grin_wallet_controller::controller::ForeignAPIHandlerV2; use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl, HTTPNodeClient}; -use grin_wallet_libwallet::{Error, InitTxArgs, IssueInvoiceTxArgs, NodeClient, Slate, SlatepackAddress, StatusMessage, TxLogEntry, TxLogEntryType, WalletInst, WalletLCProvider}; +use grin_wallet_libwallet::{Error, InitTxArgs, IssueInvoiceTxArgs, NodeClient, RetrieveTxQueryArgs, RetrieveTxQuerySortField, RetrieveTxQuerySortOrder, Slate, SlateState, StatusMessage, TxLogEntry, TxLogEntryType, WalletInst, WalletLCProvider}; use grin_wallet_libwallet::api_impl::owner::{cancel_tx, retrieve_summary_info, retrieve_txs}; use crate::AppConfig; use crate::node::{Node, NodeConfig}; use crate::wallet::{ConnectionsConfig, ExternalConnection, WalletConfig}; -use crate::wallet::types::{ConnectionMethod, WalletAccount, WalletData, WalletInstance}; +use crate::wallet::types::{ConnectionMethod, WalletAccount, WalletData, WalletInstance, WalletTransaction}; /// Contains wallet instance, configuration and state, handles wallet commands. #[derive(Clone)] @@ -86,10 +85,7 @@ pub struct Wallet { /// Flag to check if wallet repairing and restoring missing outputs is needed. repair_needed: Arc, /// Wallet repair progress in percents. - repair_progress: Arc, - - /// Identifiers for transactions to cancel. - cancel_txs: Arc>> + repair_progress: Arc } /// Default Foreign API server host. @@ -117,8 +113,7 @@ impl Wallet { data: Arc::new(RwLock::new(None)), sync_attempts: Arc::new(AtomicU8::new(0)), repair_needed: Arc::new(AtomicBool::new(false)), - repair_progress: Arc::new(AtomicU8::new(0)), - cancel_txs: Arc::new(RwLock::new(BTreeSet::new())), + repair_progress: Arc::new(AtomicU8::new(0)) } } @@ -470,15 +465,8 @@ impl Wallet { /// Parse Slatepack message into [`Slate`]. pub fn parse_slatepack(&self, message: String) -> Result { - let mut api = Owner::new(self.instance.clone().unwrap(), None); - return match parse_slatepack(&mut api, None, None, Some(message.clone())) { - Ok((slate, _)) => { - Ok(slate) - } - Err(_) => { - Err(Error::SlatepackDeser("Slatepack parse error".to_string())) - } - } + let api = Owner::new(self.instance.clone().unwrap(), None); + api.slate_from_slatepack_message(None, message, vec![]) } /// Create Slatepack message from provided slate. @@ -490,18 +478,29 @@ impl Wallet { Ok(()) })?; - // Create a directory to which slatepack files will be output. - let mut slatepack_dir = self.get_config().get_slatepacks_path(); - let slatepack_file_name = format!("{}.{}.slatepack", slate.id, slate.state); - slatepack_dir.push(slatepack_file_name); - - // Write Slatepack response into the file. + // Save slatepack. + let slatepack_dir = self.get_config().get_slatepack_path(slate.id.to_string(), &slate.state); let mut output = File::create(slatepack_dir)?; output.write_all(message.as_bytes())?; output.sync_all()?; Ok(message) } + /// Get transaction by slate id. + pub fn tx_by_slate(&self, slate: &Slate) -> Option { + if let Some(data) = self.get_data() { + let txs = data.txs.clone().iter().map(|tx| tx.clone()).filter(|tx| { + tx.data.tx_slate_id == Some(slate.id) + }).collect::>(); + return if let Some(tx) = txs.get(0) { + Some(tx.clone()) + } else { + None + } + } + None + } + /// Initialize a transaction to send amount, return request for funds receiver. pub fn send(&self, amount: u64) -> Result { let config = self.get_config(); @@ -513,7 +512,7 @@ impl Wallet { selection_strategy_is_use_all: false, ..Default::default() }; - let mut api = Owner::new(self.instance.clone().unwrap(), None); + let api = Owner::new(self.instance.clone().unwrap(), None); let slate = api.init_send_tx(None, args)?; // Lock outputs to for this transaction. @@ -535,7 +534,7 @@ impl Wallet { amount, target_slate_version: None, }; - let mut api = Owner::new(self.instance.clone().unwrap(), None); + let api = Owner::new(self.instance.clone().unwrap(), None); let slate = api.issue_invoice_tx(None, args)?; // Create Slatepack message response. @@ -558,7 +557,7 @@ impl Wallet { selection_strategy_is_use_all: false, ..Default::default() }; - let mut api = Owner::new(self.instance.clone().unwrap(), None); + let api = Owner::new(self.instance.clone().unwrap(), None); let slate = api.process_invoice_tx(None, &slate, args)?; api.tx_lock_outputs(None, &slate)?; @@ -574,7 +573,7 @@ impl Wallet { /// Handle message to receive funds, return response to sender. pub fn receive(&self, message: String) -> Result { let mut slate = self.parse_slatepack(message)?; - let mut api = Owner::new(self.instance.clone().unwrap(), None); + let api = Owner::new(self.instance.clone().unwrap(), None); controller::foreign_single_use(api.wallet_inst.clone(), None, |api| { slate = api.receive_tx(&slate, Some(self.get_config().account.as_str()), None)?; Ok(()) @@ -591,37 +590,50 @@ impl Wallet { /// Finalize transaction from provided message as sender or invoice issuer with Dandelion. pub fn finalize(&self, message: String, dandelion: bool) -> Result { let mut slate = self.parse_slatepack(message)?; - let mut api = Owner::new(self.instance.clone().unwrap(), None); + 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)?; // Sync wallet info. self.sync(); - Ok(slate) } + /// Post transaction to blockchain. + pub fn post(&self, slate: &Slate, dandelion: bool) -> Result<(), Error> { + // Post transaction to blockchain. + let api = Owner::new(self.instance.clone().unwrap(), None); + api.post_tx(None, slate, dandelion)?; + // Sync wallet info. + self.sync(); + Ok(()) + } + /// Cancel transaction. pub fn cancel(&mut self, id: u32) { + let instance = self.instance.clone().unwrap(); + let _ = cancel_tx(instance, None, &None, Some(id), None); // Set cancelling status. { - let mut cancelling_w = self.cancel_txs.write().unwrap(); - cancelling_w.insert(id); + let mut w_data = self.data.write().unwrap(); + let mut data = w_data.clone().unwrap(); + let txs = data.txs.iter_mut().map(|tx| { + if tx.data.id == id { + tx.data.tx_type = if tx.data.tx_type == TxLogEntryType::TxReceived { + TxLogEntryType::TxReceivedCancelled + } else { + TxLogEntryType::TxSentCancelled + }; + } + tx.clone() + }).collect::>(); + data.txs = txs; + *w_data = Some(data); } - - // Launch tx cancelling at separate thread. - let wallet_cancel = self.clone(); - let instance = wallet_cancel.instance.clone().unwrap(); - thread::spawn(move || { - let _ = cancel_tx(instance, None, &None, Some(id), None); - // Refresh wallet info to update statuses. - wallet_cancel.sync(); - }); - } - - /// Check if transaction is cancelling. - pub fn is_cancelling(&self, id: &u32) -> bool { - let cancelling_r = self.cancel_txs.read().unwrap(); - cancelling_r.contains(id) + // Refresh wallet info to update statuses. + self.sync(); } /// Change wallet password. @@ -858,101 +870,121 @@ fn sync_wallet_data(wallet: &Wallet) { } }); + let config = wallet.get_config(); + // Retrieve wallet info. if let Some(instance) = &wallet.instance { - match retrieve_summary_info( + if let Ok(info) = retrieve_summary_info( instance.clone(), None, &Some(info_tx), true, - wallet.get_config().min_confirmations + config.min_confirmations ) { - Ok(info) => { - // Do not retrieve txs if wallet was closed. - if !wallet.is_open() { - return; - } + // Do not retrieve txs if wallet was closed. + if !wallet.is_open() { + return; + } - if wallet.info_sync_progress() == 100 { - // Retrieve accounts data. - let last_height = info.1.last_confirmed_height; - update_accounts(wallet, last_height, info.1.amount_currently_spendable); + if wallet.info_sync_progress() == 100 { + // Retrieve accounts data. + let last_height = info.1.last_confirmed_height; + update_accounts(wallet, last_height, info.1.amount_currently_spendable); - // Update txs sync progress at separate thread. - let wallet_txs = wallet.clone(); - let (txs_tx, txs_rx) = mpsc::channel::(); - thread::spawn(move || { - while let Ok(m) = txs_rx.recv() { - println!("SYNC TXS MESSAGE"); - match m { - StatusMessage::UpdatingOutputs(_) => {} - StatusMessage::UpdatingTransactions(_) => {} - StatusMessage::FullScanWarn(_) => {} - StatusMessage::Scanning(_, progress) => { - wallet_txs.txs_sync_progress.store(progress, Ordering::Relaxed); - } - StatusMessage::ScanningComplete(_) => { - wallet_txs.txs_sync_progress.store(100, Ordering::Relaxed); - } - StatusMessage::UpdateWarning(_) => {} + // Update txs sync progress at separate thread. + let wallet_txs = wallet.clone(); + let (txs_tx, txs_rx) = mpsc::channel::(); + thread::spawn(move || { + while let Ok(m) = txs_rx.recv() { + println!("SYNC TXS MESSAGE"); + match m { + StatusMessage::UpdatingOutputs(_) => {} + StatusMessage::UpdatingTransactions(_) => {} + StatusMessage::FullScanWarn(_) => {} + StatusMessage::Scanning(_, progress) => { + wallet_txs.txs_sync_progress.store(progress, Ordering::Relaxed); } + StatusMessage::ScanningComplete(_) => { + wallet_txs.txs_sync_progress.store(100, Ordering::Relaxed); + } + StatusMessage::UpdateWarning(_) => {} } - }); + } + }); - match retrieve_txs(instance.clone(), - None, - &Some(txs_tx), - true, - None, - None, - None) { - Ok(txs) => { - // Do not sync data if wallet was closed. - if !wallet.is_open() { - return; - } - // Save data if loading was completed. - if wallet.txs_sync_progress() == 100 { - // Reset attempts. - wallet.reset_sync_attempts(); + let txs_args = RetrieveTxQueryArgs { + exclude_cancelled: Some(true), + sort_field: Some(RetrieveTxQuerySortField::CreationTimestamp), + sort_order: Some(RetrieveTxQuerySortOrder::Desc), + ..Default::default() + }; + if let Ok(txs) = retrieve_txs(instance.clone(), + None, + &Some(txs_tx), + true, + None, + None, + Some(txs_args)) { + // Do not sync data if wallet was closed. + if !wallet.is_open() { + return; + } + // Save data if loading was completed. + if wallet.txs_sync_progress() == 100 { + // Reset attempts. + wallet.reset_sync_attempts(); - // Setup transactions. - let mut sort_txs = txs.1; - // Sort txs by creation date. - sort_txs.sort_by_key(|tx| -tx.creation_ts.timestamp()); - // Filter txs by current wallet account. - let mut txs = sort_txs.iter().map(|v| v.clone()).filter(|tx| { - match wallet.get_parent_key_id() { - Ok(key) => { - tx.parent_key_id == key - } - Err(_) => { - true - } - } - }).collect::>(); - // Update txs statuses. - for tx in &txs { - println!("{}", serde_json::to_string(tx).unwrap()); - if tx.tx_type == TxLogEntryType::TxSentCancelled - || tx.tx_type == TxLogEntryType::TxReceivedCancelled { - // Remove cancelling status. - let mut cancel_w = wallet.cancel_txs.write().unwrap(); - cancel_w.remove(&tx.id); - } + // Filter transactions for current account. + let filter_txs = txs.1.iter().map(|v| v.clone()).filter(|tx| { + match wallet.get_parent_key_id() { + Ok(key) => { + tx.parent_key_id == key + } + Err(_) => { + true } - - // Update wallet data. - let mut w_data = wallet.data.write().unwrap(); - *w_data = Some(WalletData { info: info.1, txs }); - return; } + }).collect::>(); + + // Create wallet txs. + let mut txs = vec![]; + for tx in &filter_txs { + println!("{}", serde_json::to_string(tx).unwrap()); + let amount = if tx.amount_debited > tx.amount_credited { + tx.amount_debited - tx.amount_credited + } else { + tx.amount_credited - tx.amount_debited + }; + + // Setup transaction broadcasting flag based on slate state. + let posting = if (tx.tx_type == TxLogEntryType::TxSent || + tx.tx_type == TxLogEntryType::TxReceived) && + !tx.confirmed && tx.tx_slate_id.is_some() { + let sl_id = tx.tx_slate_id.unwrap().to_string(); + let state = match tx.tx_type { + TxLogEntryType::TxReceived => SlateState::Invoice3, + _ => SlateState::Standard3 + }; + let slatepack_path = config.get_slatepack_path(sl_id, &state); + fs::read_to_string(slatepack_path).is_ok() + } else { + false + }; + + txs.push(WalletTransaction { + data: tx.clone(), + amount, + posting, + }) } - Err(e) => println!("error on retrieve_txs {}", e), + + // Update wallet data. + let mut w_data = wallet.data.write().unwrap(); + *w_data = Some(WalletData { info: info.1, txs }); + return; } } } - Err(e) => println!("error on retrieve_summary_info {}", e), } } -- 2.39.5 From 10d184fab24926cb1e9a8a7ae0b18d4a14b7ad0f Mon Sep 17 00:00:00 2001 From: ardocrat Date: Sat, 20 Apr 2024 23:49:19 +0300 Subject: [PATCH 05/46] slatepacks: better error handling and messages on parsing --- locales/en.yml | 4 +- locales/ru.yml | 4 +- src/gui/views/wallets/wallet/messages.rs | 134 +++++++++++++++-------- src/gui/views/wallets/wallet/txs.rs | 7 +- src/wallet/config.rs | 6 +- src/wallet/wallet.rs | 21 +++- 6 files changed, 112 insertions(+), 64 deletions(-) diff --git a/locales/en.yml b/locales/en.yml index b525f7b..3e0efce 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -77,8 +77,8 @@ wallets: parse_s1_slatepack_desc: 'To receive %{amount} ツ send this message to the sender:' parse_s2_slatepack_desc: 'Finalize transaction to send %{amount} ツ' parse_s3_slatepack_desc: 'Post transaction to finalize sending of %{amount} ツ' - response_slatepack_err: 'An error occurred during creation of the response, check input data:' - response_exists_err: 'Such transaction already exists:' + resp_slatepack_err: 'An error occurred during creation of the response, check input data:' + resp_exists_err: 'Such transaction already exists.' create_request_desc: 'Create request to send or receive the funds:' send_request_desc: 'You have created a request to send %{amount} ツ. Send this message to the receiver of funds:' send_slatepack_err: An error occurred during creation of request to send funds, check input data. diff --git a/locales/ru.yml b/locales/ru.yml index 69fb776..7390f30 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -77,8 +77,8 @@ wallets: parse_s1_slatepack_desc: 'Для получения %{amount} ツ отправьте это сообщение отправителю:' parse_s2_slatepack_desc: 'Завершите транзакцию для отправки %{amount} ツ' parse_s3_slatepack_desc: 'Опубликуйте транзакцию для завершения отправки %{amount} ツ' - response_slatepack_err: 'Во время создания ответа произошла ошибка, проверьте входные данные:' - response_exists_err: 'Такая транзакция уже существует:' + resp_slatepack_err: 'Во время создания ответа произошла ошибка, проверьте входные данные:' + resp_exists_err: 'Такая транзакция уже существует.' create_request_desc: 'Cоздать запрос на получение или отправку средств:' send_request_desc: 'Вы создали запрос на отправку %{amount} ツ. Отправьте это сообщение получателю средств:' send_slatepack_err: Во время создания запроса на отправку средств произошла ошибка, проверьте входные данные. diff --git a/src/gui/views/wallets/wallet/messages.rs b/src/gui/views/wallets/wallet/messages.rs index 98302d0..80e9e42 100644 --- a/src/gui/views/wallets/wallet/messages.rs +++ b/src/gui/views/wallets/wallet/messages.rs @@ -191,10 +191,10 @@ impl WalletMessages { if let Some(err) = &self.message_error { ui.label(RichText::new(err.text()).size(16.0).color(Colors::RED)); } else { - let desc_text = if response_empty && self.message_slate.is_none() { + let desc_text = if self.message_slate.is_none() { t!("wallets.input_slatepack_desc") } else { - let slate = self.message_slate.as_ref().unwrap(); + let mut slate = self.message_slate.clone().unwrap(); let amount = amount_to_hr_string(slate.amount, true); match slate.state { SlateState::Standard1 => { @@ -386,51 +386,8 @@ impl WalletMessages { } if let Ok(mut slate) = wallet.parse_slatepack(self.message_edit.clone()) { println!("parse_message: {}", slate); - // Make operation based on incoming state status. - match slate.state { - SlateState::Standard1 => { - if let Ok(resp) = wallet.receive(self.message_edit.clone()) { - self.response_edit = resp; - } else { - // Check if tx with same slate id already exists. - let exists_tx = wallet.tx_by_slate(&slate).is_some(); - if exists_tx { - return; - } - // Set default response error message. - self.message_error = Some( - MessageError::Response(t!("wallets.response_slatepack_err")) - ); - } - } - SlateState::Invoice1 => { - match wallet.pay(self.message_edit.clone()) { - Ok(resp) => { - self.response_edit = resp; - } - Err(err) => { - match err { - grin_wallet_libwallet::Error::NotEnoughFunds {..} => { - let amount = amount_to_hr_string(slate.amount, true); - let a_t = t!("wallets.pay_balance_error", "amount" => amount); - self.message_error = Some(MessageError::Other(a_t)); - } - _ => { - self.message_error = Some( - MessageError::Response(t!("wallets.response_slatepack_err")) - ); - } - } - } - } - } - _ => { - self.response_edit = "".to_string(); - } - } - - // Try to get amount from transaction by id. + // Try to setup empty amount from transaction by id. if slate.amount == 0 { let _ = wallet.get_data().unwrap().txs.clone().iter().map(|tx| { if tx.data.tx_slate_id == Some(slate.id) { @@ -441,10 +398,91 @@ impl WalletMessages { tx }).collect::>(); } - self.message_slate = Some(slate.clone()); + + if slate.amount == 0 { + self.message_error = Some( + MessageError::Response(t!("wallets.resp_slatepack_err")) + ); + return; + } + + // Make operation based on incoming state status. + match slate.state { + SlateState::Standard1 | SlateState::Invoice1 => { + let resp = if slate.state == SlateState::Standard1 { + wallet.receive(self.message_edit.clone()) + } else { + wallet.pay(self.message_edit.clone()) + }; + if resp.is_ok() { + self.response_edit = resp.unwrap(); + } else { + // Check if tx with same slate id already exists. + let exists_tx = wallet.tx_by_slate(&slate).is_some(); + if exists_tx { + let mut sl = slate.clone(); + sl.state = if sl.state == SlateState::Standard1 { + SlateState::Standard2 + } else { + SlateState::Invoice2 + }; + match wallet.read_slatepack(&sl) { + None => { + self.message_error = Some( + MessageError::Response(t!("wallets.resp_slatepack_err")) + ); + } + Some(sp) => { + self.message_slate = Some(slate); + self.response_edit = sp; + } + } + return; + } + + // Set default response error message. + self.message_error = Some( + MessageError::Response(t!("wallets.resp_slatepack_err")) + ); + } + } + SlateState::Standard2 | SlateState::Invoice2 => { + // Check if slatepack with same id and state already exists. + let mut sl = slate.clone(); + sl.state = if sl.state == SlateState::Standard2 { + SlateState::Standard1 + } else { + SlateState::Invoice1 + }; + match wallet.read_slatepack(&sl) { + None => { + match wallet.read_slatepack(&slate) { + None => { + self.message_error = Some( + MessageError::Response(t!("wallets.resp_slatepack_err")) + ); + } + Some(sp) => { + self.message_slate = Some(sl); + self.response_edit = sp; + return; + } + } + } + Some(_) => { + self.message_slate = Some(slate.clone()); + return; + } + } + } + _ => { + self.response_edit = "".to_string(); + } + } + self.message_slate = Some(slate); } else { self.message_slate = None; - self.message_error = Some(MessageError::Parse(t!("wallets.response_slatepack_err"))); + self.message_error = Some(MessageError::Parse(t!("wallets.resp_slatepack_err"))); } } diff --git a/src/gui/views/wallets/wallet/txs.rs b/src/gui/views/wallets/wallet/txs.rs index 93128cf..5c16c78 100644 --- a/src/gui/views/wallets/wallet/txs.rs +++ b/src/gui/views/wallets/wallet/txs.rs @@ -230,8 +230,9 @@ 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.data.kernel_excess.is_some() && - tx.data.tx_type == TxLogEntryType::TxReceived { + } else if tx.posting || (tx.data.kernel_excess.is_some() && + (tx.data.tx_type == TxLogEntryType::TxReceived || + tx.data.tx_type == TxLogEntryType::TxSent)) { format!("{} {}", DOTS_THREE_CIRCLE, t!("wallets.tx_finalizing")) } else { match tx.data.tx_type { @@ -253,7 +254,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 { + if data.info.last_confirmed_height - tx_height > min_conf + 1 { let text = if tx.data.tx_type == TxLogEntryType::TxSent { t!("wallets.tx_sent") } else { diff --git a/src/wallet/config.rs b/src/wallet/config.rs index ce1d80f..7d5686e 100644 --- a/src/wallet/config.rs +++ b/src/wallet/config.rs @@ -17,7 +17,7 @@ use std::path::PathBuf; use std::string::ToString; use grin_core::global::ChainTypes; -use grin_wallet_libwallet::{SlateState}; +use grin_wallet_libwallet::{Slate}; use serde_derive::{Deserialize, Serialize}; use crate::{AppConfig, Settings}; @@ -122,13 +122,13 @@ impl WalletConfig { } /// Get Slatepacks data path for current wallet. - pub fn get_slatepack_path(&self, id: String, state: &SlateState) -> PathBuf { + pub fn get_slatepack_path(&self, slate: &Slate) -> PathBuf { let mut slatepack_dir = PathBuf::from(self.get_data_path()); slatepack_dir.push(SLATEPACKS_DIR_NAME); if !slatepack_dir.exists() { let _ = fs::create_dir_all(slatepack_dir.clone()); } - let slatepack_file_name = format!("{}.{}.slatepack", id, state); + let slatepack_file_name = format!("{}.{}.slatepack", slate.id, slate.state); slatepack_dir.push(slatepack_file_name); slatepack_dir } diff --git a/src/wallet/wallet.rs b/src/wallet/wallet.rs index 25d5aec..ad72e01 100644 --- a/src/wallet/wallet.rs +++ b/src/wallet/wallet.rs @@ -479,13 +479,22 @@ impl Wallet { })?; // Save slatepack. - let slatepack_dir = self.get_config().get_slatepack_path(slate.id.to_string(), &slate.state); + let slatepack_dir = self.get_config().get_slatepack_path(&slate); let mut output = File::create(slatepack_dir)?; output.write_all(message.as_bytes())?; output.sync_all()?; Ok(message) } + /// Read slatepack from file. + pub fn read_slatepack(&self, slate: &Slate) -> Option { + let slatepack_path = self.get_config().get_slatepack_path(slate); + match fs::read_to_string(slatepack_path) { + Ok(s) => Some(s), + Err(_) => None + } + } + /// Get transaction by slate id. pub fn tx_by_slate(&self, slate: &Slate) -> Option { if let Some(data) = self.get_data() { @@ -956,17 +965,17 @@ fn sync_wallet_data(wallet: &Wallet) { tx.amount_credited - tx.amount_debited }; - // Setup transaction broadcasting flag based on slate state. + // Setup transaction posting flag based on slate state. let posting = if (tx.tx_type == TxLogEntryType::TxSent || tx.tx_type == TxLogEntryType::TxReceived) && !tx.confirmed && tx.tx_slate_id.is_some() { - let sl_id = tx.tx_slate_id.unwrap().to_string(); - let state = match tx.tx_type { + let mut slate = Slate::blank(1, false); + slate.id = tx.tx_slate_id.unwrap(); + slate.state = match tx.tx_type { TxLogEntryType::TxReceived => SlateState::Invoice3, _ => SlateState::Standard3 }; - let slatepack_path = config.get_slatepack_path(sl_id, &state); - fs::read_to_string(slatepack_path).is_ok() + wallet.read_slatepack(&slate).is_some() } else { false }; -- 2.39.5 From f42fd94281d3a816c01d3fc81c519cb849776b3f Mon Sep 17 00:00:00 2001 From: ardocrat Date: Sun, 21 Apr 2024 12:04:23 +0300 Subject: [PATCH 06/46] wallet: posting status for two transactions with same slate id --- src/wallet/wallet.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/wallet/wallet.rs b/src/wallet/wallet.rs index ad72e01..cc25c57 100644 --- a/src/wallet/wallet.rs +++ b/src/wallet/wallet.rs @@ -956,7 +956,7 @@ fn sync_wallet_data(wallet: &Wallet) { }).collect::>(); // Create wallet txs. - let mut txs = vec![]; + let mut txs: Vec = vec![]; for tx in &filter_txs { println!("{}", serde_json::to_string(tx).unwrap()); let amount = if tx.amount_debited > tx.amount_credited { @@ -969,13 +969,28 @@ fn sync_wallet_data(wallet: &Wallet) { let posting = if (tx.tx_type == TxLogEntryType::TxSent || tx.tx_type == TxLogEntryType::TxReceived) && !tx.confirmed && tx.tx_slate_id.is_some() { + // Create slate to check existing file. let mut slate = Slate::blank(1, false); slate.id = tx.tx_slate_id.unwrap(); slate.state = match tx.tx_type { TxLogEntryType::TxReceived => SlateState::Invoice3, _ => SlateState::Standard3 }; - wallet.read_slatepack(&slate).is_some() + + // Setup posting status if we have other tx with same slate id. + let mut same_tx_posting = false; + for t in &mut txs { + if t.data.tx_slate_id == tx.tx_slate_id && + tx.tx_type != t.data.tx_type { + same_tx_posting = t.posting || + wallet.read_slatepack(&slate).is_some(); + if same_tx_posting && !t.posting { + t.posting = true; + } + break; + } + } + same_tx_posting || wallet.read_slatepack(&slate).is_some() } else { false }; -- 2.39.5 From 1b7d96eff577710b63f6703cf423ff1c5bea2dc6 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Sun, 21 Apr 2024 19:59:12 +0300 Subject: [PATCH 07/46] 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; } } -- 2.39.5 From 792b64951a710ec80658c430176f1a1c054ba17e Mon Sep 17 00:00:00 2001 From: ardocrat Date: Sun, 21 Apr 2024 20:42:01 +0300 Subject: [PATCH 08/46] android: move to separate folder, hide keyboard at request modal --- .gitignore | 9 ++++----- {app => android/app}/.gitignore | 0 {app => android/app}/build.gradle | 0 {app => android/app}/proguard-rules.pro | 0 {app => android/app}/src/main/AndroidManifest.xml | 0 .../app}/src/main/ic_launcher-playstore.png | Bin .../main/java/mw/gri/android/BackgroundService.java | 0 .../src/main/java/mw/gri/android/MainActivity.java | 0 .../app}/src/main/java/mw/gri/android/Utils.java | 0 .../src/main/res/drawable-hdpi/ic_stat_name.png | Bin .../src/main/res/drawable-mdpi/ic_stat_name.png | Bin .../src/main/res/drawable-xhdpi/ic_stat_name.png | Bin .../src/main/res/drawable-xxhdpi/ic_stat_name.png | Bin .../src/main/res/drawable-xxxhdpi/ic_stat_name.png | Bin .../src/main/res/mipmap-anydpi-v26/ic_launcher.xml | 0 .../res/mipmap-anydpi-v26/ic_launcher_round.xml | 0 .../app}/src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../main/res/mipmap-hdpi/ic_launcher_foreground.png | Bin .../src/main/res/mipmap-hdpi/ic_launcher_round.png | Bin .../app}/src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../main/res/mipmap-mdpi/ic_launcher_foreground.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher_round.png | Bin .../app}/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../res/mipmap-xhdpi/ic_launcher_foreground.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher_round.png | Bin .../app}/src/main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../res/mipmap-xxhdpi/ic_launcher_foreground.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../res/mipmap-xxxhdpi/ic_launcher_foreground.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher_round.png | Bin {app => android/app}/src/main/res/values/colors.xml | 0 .../src/main/res/values/ic_launcher_background.xml | 0 {app => android/app}/src/main/res/values/themes.xml | 0 build.gradle => android/build.gradle | 0 gradle.properties => android/gradle.properties | 0 .../gradle}/wrapper/gradle-wrapper.jar | Bin .../gradle}/wrapper/gradle-wrapper.properties | 0 gradlew => android/gradlew | 0 gradlew.bat => android/gradlew.bat | 0 settings.gradle => android/settings.gradle | 0 build_run_android.sh | 3 ++- src/gui/views/wallets/wallet/messages.rs | 2 ++ 43 files changed, 8 insertions(+), 6 deletions(-) rename {app => android/app}/.gitignore (100%) rename {app => android/app}/build.gradle (100%) rename {app => android/app}/proguard-rules.pro (100%) rename {app => android/app}/src/main/AndroidManifest.xml (100%) rename {app => android/app}/src/main/ic_launcher-playstore.png (100%) rename {app => android/app}/src/main/java/mw/gri/android/BackgroundService.java (100%) rename {app => android/app}/src/main/java/mw/gri/android/MainActivity.java (100%) rename {app => android/app}/src/main/java/mw/gri/android/Utils.java (100%) rename {app => android/app}/src/main/res/drawable-hdpi/ic_stat_name.png (100%) rename {app => android/app}/src/main/res/drawable-mdpi/ic_stat_name.png (100%) rename {app => android/app}/src/main/res/drawable-xhdpi/ic_stat_name.png (100%) rename {app => android/app}/src/main/res/drawable-xxhdpi/ic_stat_name.png (100%) rename {app => android/app}/src/main/res/drawable-xxxhdpi/ic_stat_name.png (100%) rename {app => android/app}/src/main/res/mipmap-anydpi-v26/ic_launcher.xml (100%) rename {app => android/app}/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml (100%) rename {app => android/app}/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename {app => android/app}/src/main/res/mipmap-hdpi/ic_launcher_foreground.png (100%) rename {app => android/app}/src/main/res/mipmap-hdpi/ic_launcher_round.png (100%) rename {app => android/app}/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename {app => android/app}/src/main/res/mipmap-mdpi/ic_launcher_foreground.png (100%) rename {app => android/app}/src/main/res/mipmap-mdpi/ic_launcher_round.png (100%) rename {app => android/app}/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename {app => android/app}/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png (100%) rename {app => android/app}/src/main/res/mipmap-xhdpi/ic_launcher_round.png (100%) rename {app => android/app}/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename {app => android/app}/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png (100%) rename {app => android/app}/src/main/res/mipmap-xxhdpi/ic_launcher_round.png (100%) rename {app => android/app}/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename {app => android/app}/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png (100%) rename {app => android/app}/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png (100%) rename {app => android/app}/src/main/res/values/colors.xml (100%) rename {app => android/app}/src/main/res/values/ic_launcher_background.xml (100%) rename {app => android/app}/src/main/res/values/themes.xml (100%) rename build.gradle => android/build.gradle (100%) rename gradle.properties => android/gradle.properties (100%) rename {gradle => android/gradle}/wrapper/gradle-wrapper.jar (100%) rename {gradle => android/gradle}/wrapper/gradle-wrapper.properties (100%) rename gradlew => android/gradlew (100%) rename gradlew.bat => android/gradlew.bat (100%) rename settings.gradle => android/settings.gradle (100%) diff --git a/.gitignore b/.gitignore index f5507e3..1fbcf5c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,15 @@ *.iml -.gradle -/local.properties +android/.gradle +android/local.properties +android/keystore +android/keystore.properties /.idea .DS_Store /build /captures .externalNativeBuild .cxx -local.properties *.so -keystore -keystore.properties # Added by cargo /target diff --git a/app/.gitignore b/android/app/.gitignore similarity index 100% rename from app/.gitignore rename to android/app/.gitignore diff --git a/app/build.gradle b/android/app/build.gradle similarity index 100% rename from app/build.gradle rename to android/app/build.gradle diff --git a/app/proguard-rules.pro b/android/app/proguard-rules.pro similarity index 100% rename from app/proguard-rules.pro rename to android/app/proguard-rules.pro diff --git a/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml similarity index 100% rename from app/src/main/AndroidManifest.xml rename to android/app/src/main/AndroidManifest.xml diff --git a/app/src/main/ic_launcher-playstore.png b/android/app/src/main/ic_launcher-playstore.png similarity index 100% rename from app/src/main/ic_launcher-playstore.png rename to android/app/src/main/ic_launcher-playstore.png diff --git a/app/src/main/java/mw/gri/android/BackgroundService.java b/android/app/src/main/java/mw/gri/android/BackgroundService.java similarity index 100% rename from app/src/main/java/mw/gri/android/BackgroundService.java rename to android/app/src/main/java/mw/gri/android/BackgroundService.java diff --git a/app/src/main/java/mw/gri/android/MainActivity.java b/android/app/src/main/java/mw/gri/android/MainActivity.java similarity index 100% rename from app/src/main/java/mw/gri/android/MainActivity.java rename to android/app/src/main/java/mw/gri/android/MainActivity.java diff --git a/app/src/main/java/mw/gri/android/Utils.java b/android/app/src/main/java/mw/gri/android/Utils.java similarity index 100% rename from app/src/main/java/mw/gri/android/Utils.java rename to android/app/src/main/java/mw/gri/android/Utils.java diff --git a/app/src/main/res/drawable-hdpi/ic_stat_name.png b/android/app/src/main/res/drawable-hdpi/ic_stat_name.png similarity index 100% rename from app/src/main/res/drawable-hdpi/ic_stat_name.png rename to android/app/src/main/res/drawable-hdpi/ic_stat_name.png diff --git a/app/src/main/res/drawable-mdpi/ic_stat_name.png b/android/app/src/main/res/drawable-mdpi/ic_stat_name.png similarity index 100% rename from app/src/main/res/drawable-mdpi/ic_stat_name.png rename to android/app/src/main/res/drawable-mdpi/ic_stat_name.png diff --git a/app/src/main/res/drawable-xhdpi/ic_stat_name.png b/android/app/src/main/res/drawable-xhdpi/ic_stat_name.png similarity index 100% rename from app/src/main/res/drawable-xhdpi/ic_stat_name.png rename to android/app/src/main/res/drawable-xhdpi/ic_stat_name.png diff --git a/app/src/main/res/drawable-xxhdpi/ic_stat_name.png b/android/app/src/main/res/drawable-xxhdpi/ic_stat_name.png similarity index 100% rename from app/src/main/res/drawable-xxhdpi/ic_stat_name.png rename to android/app/src/main/res/drawable-xxhdpi/ic_stat_name.png diff --git a/app/src/main/res/drawable-xxxhdpi/ic_stat_name.png b/android/app/src/main/res/drawable-xxxhdpi/ic_stat_name.png similarity index 100% rename from app/src/main/res/drawable-xxxhdpi/ic_stat_name.png rename to android/app/src/main/res/drawable-xxxhdpi/ic_stat_name.png diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml similarity index 100% rename from app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png rename to android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-hdpi/ic_launcher_round.png rename to android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-mdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png rename to android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-mdpi/ic_launcher_round.png rename to android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png rename to android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-xhdpi/ic_launcher_round.png rename to android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png rename to android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png rename to android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png similarity index 100% rename from app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png rename to android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png rename to android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml similarity index 100% rename from app/src/main/res/values/colors.xml rename to android/app/src/main/res/values/colors.xml diff --git a/app/src/main/res/values/ic_launcher_background.xml b/android/app/src/main/res/values/ic_launcher_background.xml similarity index 100% rename from app/src/main/res/values/ic_launcher_background.xml rename to android/app/src/main/res/values/ic_launcher_background.xml diff --git a/app/src/main/res/values/themes.xml b/android/app/src/main/res/values/themes.xml similarity index 100% rename from app/src/main/res/values/themes.xml rename to android/app/src/main/res/values/themes.xml diff --git a/build.gradle b/android/build.gradle similarity index 100% rename from build.gradle rename to android/build.gradle diff --git a/gradle.properties b/android/gradle.properties similarity index 100% rename from gradle.properties rename to android/gradle.properties diff --git a/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from gradle/wrapper/gradle-wrapper.jar rename to android/gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from gradle/wrapper/gradle-wrapper.properties rename to android/gradle/wrapper/gradle-wrapper.properties diff --git a/gradlew b/android/gradlew similarity index 100% rename from gradlew rename to android/gradlew diff --git a/gradlew.bat b/android/gradlew.bat similarity index 100% rename from gradlew.bat rename to android/gradlew.bat diff --git a/settings.gradle b/android/settings.gradle similarity index 100% rename from settings.gradle rename to android/settings.gradle diff --git a/build_run_android.sh b/build_run_android.sh index d39b007..f74d286 100755 --- a/build_run_android.sh +++ b/build_run_android.sh @@ -38,7 +38,8 @@ export CPPFLAGS="-DMDB_USE_ROBUST=0" && export CFLAGS="-DMDB_USE_ROBUST=0" \ if [ $? -eq 0 ] then - yes | mkdir -p app/src/main/jniLibs/${arch} && cp -f target/${platform}/${type}/libgrim.so app/src/main/jniLibs/${arch} + yes | mkdir -p android/app/src/main/jniLibs/${arch} && cp -f target/${platform}/${type}/libgrim.so android/app/src/main/jniLibs/${arch} + cd android ./gradlew clean ./gradlew build # Install on several devices diff --git a/src/gui/views/wallets/wallet/messages.rs b/src/gui/views/wallets/wallet/messages.rs index 1f7850d..f8d4468 100644 --- a/src/gui/views/wallets/wallet/messages.rs +++ b/src/gui/views/wallets/wallet/messages.rs @@ -619,6 +619,7 @@ impl WalletMessages { View::button(ui, t!("modal.cancel"), Colors::WHITE, || { self.amount_edit = "".to_string(); self.request_error = None; + cb.hide_keyboard(); modal.close(); }); }); @@ -718,6 +719,7 @@ impl WalletMessages { } self.amount_edit = "".to_string(); self.request_edit = "".to_string(); + cb.hide_keyboard(); modal.close(); }); }); -- 2.39.5 From e4985d758b02746d0aa1111b14213b31d09a0cca Mon Sep 17 00:00:00 2001 From: ardocrat Date: Sun, 21 Apr 2024 23:00:29 +0300 Subject: [PATCH 09/46] ui: fix translations --- locales/en.yml | 4 ++-- locales/ru.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/locales/en.yml b/locales/en.yml index 3e0efce..a87481d 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -80,9 +80,9 @@ wallets: resp_slatepack_err: 'An error occurred during creation of the response, check input data:' resp_exists_err: 'Such transaction already exists.' create_request_desc: 'Create request to send or receive the funds:' - send_request_desc: 'You have created a request to send %{amount} ツ. Send this message to the receiver of funds:' + send_request_desc: 'You have created a request to send %{amount} ツ. Send this message to the receiver:' send_slatepack_err: An error occurred during creation of request to send funds, check input data. - invoice_desc: 'You have created request to receive %{amount} ツ. Send this message to the sender of funds:' + invoice_desc: 'You have created request to receive %{amount} ツ. Send this message to the sender:' invoice_slatepack_err: An error occurred during issuing of the invoice, check input data. finalize_slatepack_err: 'An error occurred during finalization, check input data:' finalize: Finalize diff --git a/locales/ru.yml b/locales/ru.yml index 7390f30..3d35322 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -80,9 +80,9 @@ wallets: resp_slatepack_err: 'Во время создания ответа произошла ошибка, проверьте входные данные:' resp_exists_err: 'Такая транзакция уже существует.' create_request_desc: 'Cоздать запрос на получение или отправку средств:' - send_request_desc: 'Вы создали запрос на отправку %{amount} ツ. Отправьте это сообщение получателю средств:' + send_request_desc: 'Вы создали запрос на отправку %{amount} ツ. Отправьте это сообщение получателю:' send_slatepack_err: Во время создания запроса на отправку средств произошла ошибка, проверьте входные данные. - invoice_desc: 'Вы создали запрос на получение %{amount} ツ. Отправьте это сообщение отправителю средств:' + invoice_desc: 'Вы создали запрос на получение %{amount} ツ. Отправьте это сообщение отправителю:' invoice_slatepack_err: Во время выставления счёта произошла ошибка, проверьте входные данные. finalize_slatepack_err: 'Во время завершения произошла ошибка, проверьте входные данные:' finalize: Завершить -- 2.39.5 From 7558eebc1824ccd2ab7deb85f2a05c841132ca73 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Sun, 21 Apr 2024 23:37:52 +0300 Subject: [PATCH 10/46] fix: tx icon at sent status --- src/gui/views/wallets/wallet/txs.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gui/views/wallets/wallet/txs.rs b/src/gui/views/wallets/wallet/txs.rs index 2158a3a..a32e015 100644 --- a/src/gui/views/wallets/wallet/txs.rs +++ b/src/gui/views/wallets/wallet/txs.rs @@ -18,7 +18,7 @@ use grin_core::core::amount_to_hr_string; use grin_wallet_libwallet::{Slate, SlateState, TxLogEntryType}; use crate::gui::Colors; -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::icons::{ARROW_CIRCLE_DOWN, ARROW_CIRCLE_UP, 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; @@ -282,12 +282,12 @@ fn tx_item_ui(ui: &mut egui::Ui, }, TxLogEntryType::TxSent | TxLogEntryType::TxReceived => { if data.info.last_confirmed_height - tx_height > min_conf { - let text = if tx.data.tx_type == TxLogEntryType::TxSent { - t!("wallets.tx_sent") + let (icon, text) = if tx.data.tx_type == TxLogEntryType::TxSent { + (ARROW_CIRCLE_UP, t!("wallets.tx_sent")) } else { - t!("wallets.tx_received") + (ARROW_CIRCLE_DOWN, t!("wallets.tx_received")) }; - format!("{} {}", ARROW_CIRCLE_DOWN, text) + format!("{} {}", icon, text) } else { let h = data.info.last_confirmed_height; let left_conf = h - tx_height; -- 2.39.5 From 49aeabf9d3adc1ee18947e080f35c880ffa17ac7 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Sun, 21 Apr 2024 23:43:00 +0300 Subject: [PATCH 11/46] ui: clear button at message response, color text button, fix translation --- locales/ru.yml | 2 +- src/gui/views/views.rs | 36 ++++++-- src/gui/views/wallets/wallet/messages.rs | 113 +++++++++++++---------- 3 files changed, 91 insertions(+), 60 deletions(-) diff --git a/locales/ru.yml b/locales/ru.yml index 3d35322..db9bc65 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -79,7 +79,7 @@ wallets: parse_s3_slatepack_desc: 'Опубликуйте транзакцию для завершения отправки %{amount} ツ' resp_slatepack_err: 'Во время создания ответа произошла ошибка, проверьте входные данные:' resp_exists_err: 'Такая транзакция уже существует.' - create_request_desc: 'Cоздать запрос на получение или отправку средств:' + create_request_desc: 'Cоздать запрос на отправку или получение средств:' send_request_desc: 'Вы создали запрос на отправку %{amount} ツ. Отправьте это сообщение получателю:' send_slatepack_err: Во время создания запроса на отправку средств произошла ошибка, проверьте входные данные. invoice_desc: 'Вы создали запрос на получение %{amount} ツ. Отправьте это сообщение отправителю:' diff --git a/src/gui/views/views.rs b/src/gui/views/views.rs index 98c0bcc..96c2756 100644 --- a/src/gui/views/views.rs +++ b/src/gui/views/views.rs @@ -205,21 +205,41 @@ impl View { }); } - /// Draw [`Button`] with specified background fill color. - pub fn button(ui: &mut egui::Ui, text: String, fill: Color32, action: impl FnOnce()) { - let button_text = Self::ellipsize(text.to_uppercase(), 17.0, Colors::TEXT_BUTTON); - let br = Button::new(button_text) + /// Draw [`Button`] with specified background fill and text color. + fn button_resp(ui: &mut egui::Ui, text: String, text_color: Color32, bg: Color32) -> Response { + let button_text = Self::ellipsize(text.to_uppercase(), 17.0, text_color); + Button::new(button_text) .stroke(Self::DEFAULT_STROKE) - .fill(fill) + .fill(bg) .ui(ui) - .on_hover_cursor(CursorIcon::PointingHand); + .on_hover_cursor(CursorIcon::PointingHand) + } + + /// Draw [`Button`] with specified background fill color and default text color. + pub fn button(ui: &mut egui::Ui, text: String, fill: Color32, action: impl FnOnce()) { + let br = Self::button_resp(ui, text, Colors::TEXT_BUTTON, fill); if Self::touched(ui, br) { (action)(); } } - /// Draw [`Button`] with specified background fill color. - pub fn button_ui(ui: &mut egui::Ui, text: String, fill: Color32, action: impl FnOnce(&mut egui::Ui)) { + /// Draw [`Button`] with specified background fill color and text color. + pub fn colored_text_button(ui: &mut egui::Ui, + text: String, + text_color: Color32, + fill: Color32, + action: impl FnOnce()) { + let br = Self::button_resp(ui, text, text_color, fill); + if Self::touched(ui, br) { + (action)(); + } + } + + /// Draw [`Button`] with specified background fill color and ui at callback. + pub fn button_ui(ui: &mut egui::Ui, + text: String, + fill: Color32, + action: impl FnOnce(&mut egui::Ui)) { let button_text = Self::ellipsize(text.to_uppercase(), 17.0, Colors::TEXT_BUTTON); let br = Button::new(button_text) .stroke(Self::DEFAULT_STROKE) diff --git a/src/gui/views/wallets/wallet/messages.rs b/src/gui/views/wallets/wallet/messages.rs index f8d4468..cdfd483 100644 --- a/src/gui/views/wallets/wallet/messages.rs +++ b/src/gui/views/wallets/wallet/messages.rs @@ -194,7 +194,7 @@ impl WalletMessages { let desc_text = if self.message_slate.is_none() { t!("wallets.input_slatepack_desc") } else { - let mut slate = self.message_slate.clone().unwrap(); + let slate = self.message_slate.clone().unwrap(); let amount = amount_to_hr_string(slate.amount, true); match slate.state { SlateState::Standard1 => { @@ -285,8 +285,8 @@ impl WalletMessages { self.message_slate = None; }); } else { - let clear_text = format!("{} {}", PROHIBIT, t!("modal.cancel")); - View::button(ui, clear_text, Colors::BUTTON, || { + let cancel = format!("{} {}", PROHIBIT, t!("modal.cancel")); + View::colored_text_button(ui, cancel, Colors::RED, Colors::BUTTON, || { let slate = self.message_slate.clone().unwrap(); if let Some(tx) = wallet.tx_by_slate(&slate) { wallet.cancel(tx.data.id); @@ -297,8 +297,8 @@ impl WalletMessages { }); } } else { - let paste_text = format!("{} {}", CLIPBOARD_TEXT, t!("paste")); - View::button(ui, paste_text, Colors::BUTTON, || { + let paste = format!("{} {}", CLIPBOARD_TEXT, t!("paste")); + View::button(ui, paste, Colors::BUTTON, || { let buf = cb.get_string_from_buffer(); let previous = self.message_edit.clone(); self.message_edit = buf.clone(); @@ -366,6 +366,19 @@ impl WalletMessages { }); } }); + + ui.add_space(12.0); + + // Draw clear button on response. + if !self.response_edit.is_empty() && self.message_slate.is_some() { + let clear_text = format!("{} {}", BROOM, t!("clear")); + View::button(ui, clear_text, Colors::BUTTON, || { + self.message_error = None; + self.message_edit.clear(); + self.response_edit.clear(); + self.message_slate = None; + }); + } }); // Draw setup of ability to post transaction with Dandelion. @@ -502,7 +515,7 @@ impl WalletMessages { columns[0].vertical_centered_justified(|ui| { // Draw send request creation button. let send_text = format!("{} {}", UPLOAD, t!("wallets.send")); - View::button(ui, send_text.clone(), Colors::BUTTON, || { + View::button(ui, send_text, Colors::BUTTON, || { // Setup modal values. self.send_request = true; self.amount_edit = "".to_string(); @@ -518,7 +531,7 @@ impl WalletMessages { columns[1].vertical_centered_justified(|ui| { // Draw invoice request creation button. let receive_text = format!("{} {}", DOWNLOAD, t!("wallets.receive")); - View::button(ui, receive_text.clone(), Colors::BUTTON, || { + View::button(ui, receive_text, Colors::BUTTON, || { // Setup modal values. self.send_request = false; self.amount_edit = "".to_string(); @@ -608,57 +621,55 @@ impl WalletMessages { }); } - // Show modal buttons. ui.add_space(12.0); - ui.scope(|ui| { - // 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| { - View::button(ui, t!("modal.cancel"), Colors::WHITE, || { - self.amount_edit = "".to_string(); - self.request_error = None; - cb.hide_keyboard(); - modal.close(); - }); + // 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| { + View::button(ui, t!("modal.cancel"), Colors::WHITE, || { + self.amount_edit = "".to_string(); + self.request_error = None; + cb.hide_keyboard(); + modal.close(); }); - columns[1].vertical_centered_justified(|ui| { - // Button to create Slatepack message for request. - View::button(ui, t!("continue"), Colors::WHITE, || { - if let Ok(a) = amount_from_hr_string(self.amount_edit.as_str()) { - let message = if self.send_request { - wallet.send(a) - } else { - wallet.issue_invoice(a) - }; - match message { - Ok(message) => { - self.request_edit = message; - cb.hide_keyboard(); - } - Err(err) => { - match err { - grin_wallet_libwallet::Error::NotEnoughFunds { .. } => { - let m = t!( + }); + columns[1].vertical_centered_justified(|ui| { + // Button to create Slatepack message for request. + View::button(ui, t!("continue"), Colors::WHITE, || { + if let Ok(a) = amount_from_hr_string(self.amount_edit.as_str()) { + let message = if self.send_request { + wallet.send(a) + } else { + wallet.issue_invoice(a) + }; + match message { + Ok(message) => { + self.request_edit = message; + cb.hide_keyboard(); + } + Err(err) => { + match err { + grin_wallet_libwallet::Error::NotEnoughFunds { .. } => { + let m = t!( "wallets.pay_balance_error", "amount" => self.amount_edit ); - self.request_error = Some(MessageError::Other(m)); - } - _ => { - let m = t!("wallets.invoice_slatepack_err"); - self.request_error = Some(MessageError::Other(m)); - } + self.request_error = Some(MessageError::Other(m)); + } + _ => { + let m = t!("wallets.invoice_slatepack_err"); + self.request_error = Some(MessageError::Other(m)); } } } - } else { - self.request_error = Some( - MessageError::Other(t!("wallets.invoice_slatepack_err")) - ); } - }); + } else { + self.request_error = Some( + MessageError::Other(t!("wallets.invoice_slatepack_err")) + ); + } }); }); }); @@ -702,16 +713,16 @@ impl WalletMessages { View::horizontal_line(ui, Colors::ITEM_STROKE); }); - // 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, || { + let cancel = format!("{} {}", PROHIBIT, t!("modal.cancel")); + View::colored_text_button(ui, cancel, Colors::RED, 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); -- 2.39.5 From 372fa64218fab5cd593dfb14ccee1a94f9630e1a Mon Sep 17 00:00:00 2001 From: ardocrat Date: Sun, 21 Apr 2024 23:45:24 +0300 Subject: [PATCH 12/46] readme: fix link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ded89f6..6e11d2d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Grim +# Grim Cross-platform GUI for [GRiN ツ](https://grin.mw) in [Rust](https://www.rust-lang.org/) for maximum compatibility with original [Mimblewimble](https://github.com/mimblewimble) implementation. Initially supported platforms are Linux, Mac, Windows, limited Android and possible web support with help of [egui](https://github.com/emilk/egui) - GUI library in pure Rust. -- 2.39.5 From 5393e140d75a320b830953e5d00099214d2e68ad Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 22 Apr 2024 02:41:07 +0300 Subject: [PATCH 13/46] ui: fix tx list item padding --- src/gui/views/wallets/wallet/txs.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/gui/views/wallets/wallet/txs.rs b/src/gui/views/wallets/wallet/txs.rs index a32e015..ec8bc1e 100644 --- a/src/gui/views/wallets/wallet/txs.rs +++ b/src/gui/views/wallets/wallet/txs.rs @@ -71,7 +71,6 @@ impl WalletInfo { /// Draw transactions content. fn txs_ui(&self, ui: &mut egui::Ui, wallet: &mut Wallet) { let data = wallet.get_data().unwrap(); - let config = wallet.get_config(); let txs_size = data.txs.len(); // Show transactions info. @@ -136,7 +135,7 @@ impl WalletInfo { ui.add_space(3.0); ScrollArea::vertical() .scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible) - .id_source(Id::from("txs_content").with(config.id)) + .id_source(Id::from("txs_content").with(wallet.get_config().id)) .auto_shrink([false; 2]) .show_rows(ui, TX_ITEM_HEIGHT, txs_size, |ui, row_range| { ui.add_space(4.0); @@ -146,7 +145,7 @@ impl WalletInfo { // Setup item rounding. let item_rounding = View::item_rounding(index, txs_size, false); // Show transaction item. - tx_item_ui(ui, tx, item_rounding, config.min_confirmations, &data, wallet); + tx_item_ui(ui, tx, item_rounding, &data, wallet); } }); ui.add_space(2.0); @@ -161,12 +160,12 @@ const TX_ITEM_HEIGHT: f32 = 76.0; fn tx_item_ui(ui: &mut egui::Ui, tx: &WalletTransaction, mut rounding: Rounding, - min_conf: u64, data: &WalletData, wallet: &mut Wallet) { // Setup layout size. let mut rect = ui.available_rect_before_wrap(); - rect.min += egui::vec2(6.0, 0.0); + rect.min += egui::vec2(5.0, 0.0); + rect.max -= egui::vec2(4.0, 0.0); rect.set_height(TX_ITEM_HEIGHT); // Draw round background. @@ -175,7 +174,7 @@ fn tx_item_ui(ui: &mut egui::Ui, ui.vertical(|ui| { ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { - ui.add_space(-6.0); + ui.add_space(-5.0); // Draw button to show transaction info. rounding.nw = 0.0; rounding.sw = 0.0; -- 2.39.5 From 53f86edb34004ff49dacd754686cbdd5a00c7bd6 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 22 Apr 2024 04:20:37 +0300 Subject: [PATCH 14/46] accounts: fix list update after creation, creation inside list --- locales/en.yml | 2 +- locales/ru.yml | 2 +- src/gui/views/wallets/wallet/content.rs | 192 ++++++++++++------------ src/wallet/wallet.rs | 26 +++- 4 files changed, 120 insertions(+), 102 deletions(-) diff --git a/locales/en.yml b/locales/en.yml index a87481d..1fdeb8a 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -49,12 +49,12 @@ wallets: closing: Closing checking: Checking default_wallet: Default wallet + new_account_desc: 'Enter name of new account:' wallet_loading: Loading wallet wallet_closing: Closing wallet wallet_checking: Checking wallet tx_loading: Loading transactions default_account: Default account - create_account: Create account accounts: Accounts tx_sent: Sent tx_received: Received diff --git a/locales/ru.yml b/locales/ru.yml index db9bc65..6fb99e3 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -49,12 +49,12 @@ wallets: closing: Закрывается checking: Проверяется default_wallet: Стандартный кошелёк + new_account_desc: 'Введите название нового аккаунта:' wallet_loading: Загрузка кошелька wallet_closing: Закрытие кошелька wallet_checking: Проверка кошелька tx_loading: Загрузка транзакций default_account: Стандартный аккаунт - create_account: Создать аккаунт accounts: Аккаунты tx_sent: Отправлено tx_received: Получено diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index d92b6ea..2da3913 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -13,14 +13,13 @@ // limitations under the License. use std::time::Duration; - -use egui::{Align, Id, Layout, Margin, RichText, Rounding, ScrollArea, Widget}; +use egui::{Align, Id, Layout, Margin, RichText, ScrollArea, Widget}; use grin_chain::SyncStatus; use grin_core::core::amount_to_hr_string; use crate::AppConfig; use crate::gui::Colors; -use crate::gui::icons::{BRIDGE, CHAT_CIRCLE_TEXT, CHECK, CHECK_FAT, FILE_ARCHIVE, GEAR_FINE, LIST, PACKAGE, PLUS, POWER, REPEAT, WALLET}; +use crate::gui::icons::{BRIDGE, CHECK, CHECK_FAT, ENVELOPE_OPEN, FILES, FOLDER_USER, GEAR_FINE, HASH, PACKAGE, POWER, QR_CODE, REPEAT, USERS_THREE}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, Root, View}; use crate::gui::views::types::{ModalPosition, TextEditOptions}; @@ -35,6 +34,8 @@ pub struct WalletContent { /// List of wallet accounts for [`Modal`]. accounts: Vec, + /// Flag to check if account is creating. + account_creating: bool, /// Account label [`Modal`] value. account_label_edit: String, /// Flag to check if error occurred during account creation at [`Modal`]. @@ -48,6 +49,7 @@ impl Default for WalletContent { fn default() -> Self { Self { accounts: vec![], + account_creating: false, account_label_edit: "".to_string(), account_creation_error: false, current_tab: Box::new(WalletInfo::default()) @@ -55,8 +57,6 @@ impl Default for WalletContent { } } -/// Identifier for account creation [`Modal`]. -const CREATE_ACCOUNT_MODAL: &'static str = "create_account_modal"; /// Identifier for account list [`Modal`]. const ACCOUNT_LIST_MODAL: &'static str = "account_list_modal"; @@ -94,8 +94,8 @@ impl WalletContent { }) .show_animated_inside(ui, show_balance, |ui| { ui.vertical_centered(|ui| { - // Draw wallet tabs. - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.35, |ui| { + // Draw account info. + View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.25, |ui| { self.account_ui(ui, wallet, data.unwrap(), cb); }); }); @@ -151,14 +151,9 @@ impl WalletContent { None => {} Some(id) => { match id { - CREATE_ACCOUNT_MODAL => { - Modal::ui(ui.ctx(), |ui, modal| { - self.create_account_modal_ui(ui, wallet, modal, cb); - }); - }, ACCOUNT_LIST_MODAL => { Modal::ui(ui.ctx(), |ui, modal| { - self.account_list_modal_ui(ui, wallet, modal); + self.account_list_modal_ui(ui, wallet, modal, cb); }); } _ => {} @@ -177,36 +172,33 @@ impl WalletContent { rect.set_height(75.0); // Draw round background. let rounding = View::item_rounding(0, 2, false); - ui.painter().rect(rect, rounding, Colors::BUTTON, View::ITEM_STROKE); + ui.painter().rect(rect, rounding, Colors::BUTTON, View::HOVER_STROKE); ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { // Setup padding for item buttons. ui.style_mut().spacing.button_padding = egui::vec2(14.0, 0.0); - // Draw button to add new account. - View::item_button(ui, View::item_rounding(0, 2, true), PLUS, None, || { - // Show account creation modal. - Modal::new(CREATE_ACCOUNT_MODAL) - .position(ModalPosition::CenterTop) - .title(t!("wallets.create_account")) - .show(); - cb.show_keyboard(); + // Draw button to scan QR code. + View::item_button(ui, View::item_rounding(0, 2, true), QR_CODE, None, || { + //TODO: Scan with QR code. }); // Draw button to show list of accounts. - View::item_button(ui, Rounding::ZERO, LIST, None, || { + View::item_button(ui, View::item_rounding(1, 3, true), USERS_THREE, None, || { // Load accounts. + self.account_label_edit = "".to_string(); self.accounts = wallet.accounts(); + self.account_creating = false; // Show account list modal. Modal::new(ACCOUNT_LIST_MODAL) - .position(ModalPosition::Center) + .position(ModalPosition::CenterTop) .title(t!("wallets.accounts")) .show(); }); let layout_size = ui.available_size(); ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { - ui.add_space(6.0); + ui.add_space(8.0); ui.vertical(|ui| { ui.add_space(3.0); // Show spendable amount. @@ -223,7 +215,7 @@ impl WalletContent { } else { account.to_owned() }; - let acc_text = format!("{} {}", FILE_ARCHIVE, acc_label); + let acc_text = format!("{} {}", FOLDER_USER, acc_label); View::ellipsize_text(ui, acc_text, 15.0, Colors::TEXT); // Show confirmed height. @@ -235,75 +227,38 @@ impl WalletContent { } /// Draw account list [`Modal`] content. - fn account_list_modal_ui(&mut self, ui: &mut egui::Ui, wallet: &mut Wallet, modal: &Modal) { - ui.add_space(3.0); - - // Show list of accounts. - let size = self.accounts.len(); - ScrollArea::vertical() - .max_height(300.0) - .id_source("account_list_modal_scroll") - .auto_shrink([true; 2]) - .show_rows(ui, ACCOUNT_ITEM_HEIGHT, size, |ui, row_range| { - for index in row_range { - // Add space before the first item. - if index == 0 { - ui.add_space(4.0); - } - let acc = self.accounts.get(index).unwrap(); - account_item_ui(ui, modal, wallet, acc, index, size); - if index == size - 1 { - ui.add_space(4.0); - } - } - }); - - ui.add_space(2.0); - View::horizontal_line(ui, Colors::STROKE); - ui.add_space(6.0); - - ui.vertical_centered_justified(|ui| { - View::button(ui, t!("close"), Colors::WHITE, || { - // Close modal. - modal.close(); - }); + fn account_list_modal_ui(&mut self, + ui: &mut egui::Ui, + wallet: &mut Wallet, + modal: &Modal, + cb: &dyn PlatformCallbacks) { + if self.account_creating { ui.add_space(6.0); - }); - } - - /// Draw account creation [`Modal`] content. - fn create_account_modal_ui(&mut self, - ui: &mut egui::Ui, - wallet: &mut Wallet, - modal: &Modal, - cb: &dyn PlatformCallbacks) { - ui.add_space(6.0); - ui.vertical_centered(|ui| { - ui.label(RichText::new(t!("wallets.name")) - .size(17.0) - .color(Colors::GRAY)); - ui.add_space(8.0); - - // Draw account name edit. - let text_edit_id = Id::from(modal.id).with(wallet.get_config().id); - let text_edit_opts = TextEditOptions::new(text_edit_id); - View::text_edit(ui, cb, &mut self.account_label_edit, text_edit_opts); - - // Show error occurred during account creation.. - if self.account_creation_error { - ui.add_space(12.0); - ui.label(RichText::new(t!("error")) + ui.vertical_centered(|ui| { + ui.label(RichText::new(t!("wallets.new_account_desc")) .size(17.0) - .color(Colors::RED)); - } - ui.add_space(12.0); - }); + .color(Colors::GRAY)); + ui.add_space(8.0); + + // Draw account name edit. + let text_edit_id = Id::from(modal.id).with(wallet.get_config().id); + let text_edit_opts = TextEditOptions::new(text_edit_id); + View::text_edit(ui, cb, &mut self.account_label_edit, text_edit_opts); + + // Show error occurred during account creation.. + if self.account_creation_error { + ui.add_space(12.0); + ui.label(RichText::new(t!("error")) + .size(17.0) + .color(Colors::RED)); + } + ui.add_space(12.0); + }); - // Show modal buttons. - ui.scope(|ui| { // Setup spacing between buttons. - ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0); + ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); + // Show modal buttons. ui.columns(2, |columns| { columns[0].vertical_centered_justified(|ui| { View::button(ui, t!("modal.cancel"), Colors::WHITE, || { @@ -336,7 +291,52 @@ impl WalletContent { }); }); ui.add_space(6.0); - }); + } else { + ui.add_space(3.0); + + // Show list of accounts. + let size = self.accounts.len(); + ScrollArea::vertical() + .max_height(280.0) + .id_source("account_list_modal_scroll") + .auto_shrink([true; 2]) + .show_rows(ui, ACCOUNT_ITEM_HEIGHT, size, |ui, row_range| { + for index in row_range { + // Add space before the first item. + if index == 0 { + ui.add_space(4.0); + } + let acc = self.accounts.get(index).unwrap(); + account_item_ui(ui, modal, wallet, acc, index, size); + if index == size - 1 { + ui.add_space(4.0); + } + } + }); + + ui.add_space(2.0); + View::horizontal_line(ui, Colors::STROKE); + ui.add_space(6.0); + + // Setup spacing between buttons. + ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); + + // Show modal buttons. + ui.columns(2, |columns| { + columns[0].vertical_centered_justified(|ui| { + View::button(ui, t!("modal.cancel"), Colors::WHITE, || { + modal.close(); + }); + }); + columns[1].vertical_centered_justified(|ui| { + View::button(ui, t!("create"), Colors::WHITE, || { + self.account_creating = true; + cb.show_keyboard(); + }); + }); + }); + ui.add_space(6.0); + } } /// Draw tab buttons in the bottom of the screen. @@ -351,13 +351,13 @@ impl WalletContent { let current_type = self.current_tab.get_type(); ui.columns(4, |columns| { columns[0].vertical_centered_justified(|ui| { - View::tab_button(ui, WALLET, current_type == WalletTabType::Txs, || { + View::tab_button(ui, FILES, current_type == WalletTabType::Txs, || { self.current_tab = Box::new(WalletInfo::default()); }); }); columns[1].vertical_centered_justified(|ui| { let is_messages = current_type == WalletTabType::Messages; - View::tab_button(ui, CHAT_CIRCLE_TEXT, is_messages, || { + View::tab_button(ui, ENVELOPE_OPEN, is_messages, || { self.current_tab = Box::new( WalletMessages::new(wallet.can_use_dandelion()) ); @@ -543,10 +543,12 @@ fn account_item_ui(ui: &mut egui::Ui, } else { acc.label.to_owned() }; - View::ellipsize_text(ui, acc_label, 15.0, Colors::TEXT); + let acc_name = format!("{} {}", FOLDER_USER, acc_label); + View::ellipsize_text(ui, acc_name, 15.0, Colors::TEXT); // Show account BIP32 derivation path. - ui.label(RichText::new(acc.path.to_owned()).size(15.0).color(Colors::GRAY)); + let acc_path = format!("{} {}", HASH, acc.path); + ui.label(RichText::new(acc_path).size(15.0).color(Colors::GRAY)); ui.add_space(3.0); }); }); diff --git a/src/wallet/wallet.rs b/src/wallet/wallet.rs index a1e8a8d..e6ec17f 100644 --- a/src/wallet/wallet.rs +++ b/src/wallet/wallet.rs @@ -371,6 +371,15 @@ impl Wallet { controller::owner_single_use(None, None, Some(&mut api), |api, m| { api.create_account_path(m, label)?; + // Update account list at separate thread. + if let Some(data) = self.get_data() { + let last_height = data.info.last_confirmed_height; + let wallet = self.clone(); + thread::spawn(move || { + update_accounts(&wallet, last_height, None); + }); + } + // Sync wallet data. self.sync(); Ok(()) @@ -921,7 +930,12 @@ fn sync_wallet_data(wallet: &Wallet) { if wallet.info_sync_progress() == 100 { // Retrieve accounts data. let last_height = info.1.last_confirmed_height; - update_accounts(wallet, last_height, info.1.amount_currently_spendable); + let spendable = if wallet.get_data().is_none() { + None + } else { + Some(info.1.amount_currently_spendable) + }; + update_accounts(wallet, last_height, spendable); // Update txs sync progress at separate thread. let wallet_txs = wallet.clone(); @@ -1076,13 +1090,13 @@ fn sync_wallet_data(wallet: &Wallet) { } /// Update wallet accounts data. -fn update_accounts(wallet: &Wallet, current_height: u64, current_spendable: u64) { +fn update_accounts(wallet: &Wallet, current_height: u64, current_spendable: Option) { // Update only current account if list is not empty. - if !wallet.accounts.read().unwrap().is_empty() { + if current_spendable.is_some() { let mut accounts = wallet.accounts.read().unwrap().clone(); for mut a in accounts.iter_mut() { if a.label == wallet.get_config().account { - a.spendable_amount = current_spendable; + a.spendable_amount = current_spendable.unwrap(); } } // Save accounts data. @@ -1113,8 +1127,10 @@ fn update_accounts(wallet: &Wallet, current_height: u64, current_spendable: u64) spendable_amount, label: a.label, path: a.path.to_bip_32_string(), - }) + }); } + // Sort in reverse. + accounts.reverse(); // Save accounts data. let mut w_data = wallet.accounts.write().unwrap(); -- 2.39.5 From 43b110561471c206391dd06bb6ed41f3eb8bbcf0 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 22 Apr 2024 14:34:19 +0300 Subject: [PATCH 15/46] ui: change icons, amount format --- src/gui/views/wallets/wallet/content.rs | 10 +++++----- src/gui/views/wallets/wallet/messages.rs | 6 +++--- src/gui/views/wallets/wallet/txs.rs | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index 2da3913..662b480 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -19,7 +19,7 @@ use grin_core::core::amount_to_hr_string; use crate::AppConfig; use crate::gui::Colors; -use crate::gui::icons::{BRIDGE, CHECK, CHECK_FAT, ENVELOPE_OPEN, FILES, FOLDER_USER, GEAR_FINE, HASH, PACKAGE, POWER, QR_CODE, REPEAT, USERS_THREE}; +use crate::gui::icons::{BRIDGE, CHAT_CIRCLE_TEXT, CHECK, CHECK_FAT, FOLDER_USER, GEAR_FINE, GRAPH, PACKAGE, PATH, POWER, QR_CODE, REPEAT, USERS_THREE}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, Root, View}; use crate::gui::views::types::{ModalPosition, TextEditOptions}; @@ -351,13 +351,13 @@ impl WalletContent { let current_type = self.current_tab.get_type(); ui.columns(4, |columns| { columns[0].vertical_centered_justified(|ui| { - View::tab_button(ui, FILES, current_type == WalletTabType::Txs, || { + View::tab_button(ui, GRAPH, current_type == WalletTabType::Txs, || { self.current_tab = Box::new(WalletInfo::default()); }); }); columns[1].vertical_centered_justified(|ui| { let is_messages = current_type == WalletTabType::Messages; - View::tab_button(ui, ENVELOPE_OPEN, is_messages, || { + View::tab_button(ui, CHAT_CIRCLE_TEXT, is_messages, || { self.current_tab = Box::new( WalletMessages::new(wallet.can_use_dandelion()) ); @@ -531,7 +531,7 @@ fn account_item_ui(ui: &mut egui::Ui, ui.vertical(|ui| { ui.add_space(4.0); // Show spendable amount. - let amount = amount_to_hr_string(acc.spendable_amount, false); + let amount = amount_to_hr_string(acc.spendable_amount, true); let amount_text = format!("{} {}", amount, GRIN); ui.label(RichText::new(amount_text).size(18.0).color(Colors::BLACK)); ui.add_space(-2.0); @@ -547,7 +547,7 @@ fn account_item_ui(ui: &mut egui::Ui, View::ellipsize_text(ui, acc_name, 15.0, Colors::TEXT); // Show account BIP32 derivation path. - let acc_path = format!("{} {}", HASH, acc.path); + let acc_path = format!("{} {}", PATH, acc.path); ui.label(RichText::new(acc_path).size(15.0).color(Colors::GRAY)); ui.add_space(3.0); }); diff --git a/src/gui/views/wallets/wallet/messages.rs b/src/gui/views/wallets/wallet/messages.rs index cdfd483..fe6dbfb 100644 --- a/src/gui/views/wallets/wallet/messages.rs +++ b/src/gui/views/wallets/wallet/messages.rs @@ -19,7 +19,7 @@ use grin_wallet_libwallet::{Slate, SlateState}; use log::error; use crate::gui::Colors; -use crate::gui::icons::{BROOM, CLIPBOARD_TEXT, COPY, DOWNLOAD, PROHIBIT, UPLOAD}; +use crate::gui::icons::{BROOM, CLIPBOARD_TEXT, COPY, DOWNLOAD_SIMPLE, PROHIBIT, UPLOAD_SIMPLE}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, Root, View}; use crate::gui::views::types::{ModalPosition, TextEditOptions}; @@ -514,7 +514,7 @@ impl WalletMessages { ui.columns(2, |columns| { columns[0].vertical_centered_justified(|ui| { // Draw send request creation button. - let send_text = format!("{} {}", UPLOAD, t!("wallets.send")); + let send_text = format!("{} {}", UPLOAD_SIMPLE, t!("wallets.send")); View::button(ui, send_text, Colors::BUTTON, || { // Setup modal values. self.send_request = true; @@ -530,7 +530,7 @@ impl WalletMessages { }); columns[1].vertical_centered_justified(|ui| { // Draw invoice request creation button. - let receive_text = format!("{} {}", DOWNLOAD, t!("wallets.receive")); + let receive_text = format!("{} {}", DOWNLOAD_SIMPLE, t!("wallets.receive")); View::button(ui, receive_text, Colors::BUTTON, || { // Setup modal values. self.send_request = false; diff --git a/src/gui/views/wallets/wallet/txs.rs b/src/gui/views/wallets/wallet/txs.rs index ec8bc1e..3a9370f 100644 --- a/src/gui/views/wallets/wallet/txs.rs +++ b/src/gui/views/wallets/wallet/txs.rs @@ -18,7 +18,7 @@ use grin_core::core::amount_to_hr_string; use grin_wallet_libwallet::{Slate, SlateState, TxLogEntryType}; use crate::gui::Colors; -use crate::gui::icons::{ARROW_CIRCLE_DOWN, ARROW_CIRCLE_UP, ARROWS_CLOCKWISE, 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, ARROW_CIRCLE_UP, ARROW_CLOCKWISE, 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; @@ -201,7 +201,7 @@ fn tx_item_ui(ui: &mut egui::Ui, if can_repost { View::item_button(ui, Rounding::default(), - ARROWS_CLOCKWISE, + ARROW_CLOCKWISE, Some(Colors::GREEN), || { // Create slate to check existing file. let mut slate = Slate::blank(1, false); -- 2.39.5 From b046ea45eaf953cc70aa844e33a3888f19597a0d Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 22 Apr 2024 16:04:18 +0300 Subject: [PATCH 16/46] ui: list item buttons width --- src/gui/views/views.rs | 2 ++ src/gui/views/wallets/wallet/content.rs | 5 +---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/gui/views/views.rs b/src/gui/views/views.rs index 96c2756..f6c6193 100644 --- a/src/gui/views/views.rs +++ b/src/gui/views/views.rs @@ -263,6 +263,8 @@ impl View { let button_size = rect.size(); ui.scope(|ui| { + // Setup padding for item buttons. + ui.style_mut().spacing.button_padding = egui::vec2(14.0, 0.0); // Disable expansion on click/hover. ui.style_mut().visuals.widgets.hovered.expansion = 0.0; ui.style_mut().visuals.widgets.active.expansion = 0.0; diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index 662b480..3b78b84 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -175,9 +175,6 @@ impl WalletContent { ui.painter().rect(rect, rounding, Colors::BUTTON, View::HOVER_STROKE); ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { - // Setup padding for item buttons. - ui.style_mut().spacing.button_padding = egui::vec2(14.0, 0.0); - // Draw button to scan QR code. View::item_button(ui, View::item_rounding(0, 2, true), QR_CODE, None, || { //TODO: Scan with QR code. @@ -297,7 +294,7 @@ impl WalletContent { // Show list of accounts. let size = self.accounts.len(); ScrollArea::vertical() - .max_height(280.0) + .max_height(285.0) .id_source("account_list_modal_scroll") .auto_shrink([true; 2]) .show_rows(ui, ACCOUNT_ITEM_HEIGHT, size, |ui, row_range| { -- 2.39.5 From 6258cca7605839022f7147c4e8ebdc619d548392 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 22 Apr 2024 16:38:40 +0300 Subject: [PATCH 17/46] fix: tx list paddings --- src/gui/views/wallets/wallet/txs.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/gui/views/wallets/wallet/txs.rs b/src/gui/views/wallets/wallet/txs.rs index 3a9370f..d2452a6 100644 --- a/src/gui/views/wallets/wallet/txs.rs +++ b/src/gui/views/wallets/wallet/txs.rs @@ -132,13 +132,13 @@ impl WalletInfo { }); // Show list of transactions. - ui.add_space(3.0); + ui.add_space(4.0); ScrollArea::vertical() .scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible) .id_source(Id::from("txs_content").with(wallet.get_config().id)) .auto_shrink([false; 2]) .show_rows(ui, TX_ITEM_HEIGHT, txs_size, |ui, row_range| { - ui.add_space(4.0); + ui.add_space(3.0); View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { for index in row_range { let tx = data.txs.get(index).unwrap(); @@ -148,7 +148,6 @@ impl WalletInfo { tx_item_ui(ui, tx, item_rounding, &data, wallet); } }); - ui.add_space(2.0); }); } } @@ -164,8 +163,6 @@ fn tx_item_ui(ui: &mut egui::Ui, wallet: &mut Wallet) { // Setup layout size. let mut rect = ui.available_rect_before_wrap(); - rect.min += egui::vec2(5.0, 0.0); - rect.max -= egui::vec2(4.0, 0.0); rect.set_height(TX_ITEM_HEIGHT); // Draw round background. @@ -174,7 +171,6 @@ fn tx_item_ui(ui: &mut egui::Ui, ui.vertical(|ui| { ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { - ui.add_space(-5.0); // Draw button to show transaction info. rounding.nw = 0.0; rounding.sw = 0.0; @@ -221,7 +217,7 @@ fn tx_item_ui(ui: &mut egui::Ui, 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); + ui.add_space(8.0); ui.vertical(|ui| { ui.add_space(3.0); -- 2.39.5 From cd89b879c6c6d322df1b298179845fc4f633083b Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 22 Apr 2024 16:44:04 +0300 Subject: [PATCH 18/46] accounts: max list height --- src/gui/views/wallets/wallet/content.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index 3b78b84..0c99146 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -294,7 +294,7 @@ impl WalletContent { // Show list of accounts. let size = self.accounts.len(); ScrollArea::vertical() - .max_height(285.0) + .max_height(266.0) .id_source("account_list_modal_scroll") .auto_shrink([true; 2]) .show_rows(ui, ACCOUNT_ITEM_HEIGHT, size, |ui, row_range| { -- 2.39.5 From 4b98ce364ed55531edb840add009a9c403b1fc3c Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 22 Apr 2024 17:37:58 +0300 Subject: [PATCH 19/46] ui: node content max width --- src/gui/views/network/content.rs | 14 +- src/gui/views/network/metrics.rs | 170 +++++++++++--------- src/gui/views/network/mining.rs | 8 +- src/gui/views/network/node.rs | 258 ++++++++++++++++-------------- src/gui/views/network/settings.rs | 61 +++---- 5 files changed, 273 insertions(+), 238 deletions(-) diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index 34ab8f5..0f876ff 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -16,7 +16,7 @@ use egui::{Margin, RichText, ScrollArea, Stroke}; use crate::AppConfig; use crate::gui::Colors; -use crate::gui::icons::{CARDHOLDER, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE, PLUS_CIRCLE, POWER}; +use crate::gui::icons::{BRIEFCASE, CARDHOLDER, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE, PLUS_CIRCLE, POWER}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{ConnectionsContent, NetworkMetrics, NetworkMining, NetworkNode, NetworkSettings, Root, TitlePanel, View}; use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; @@ -64,7 +64,12 @@ impl NetworkContent { ..Default::default() }) .show_animated_inside(ui, !show_connections, |ui| { - self.tabs_ui(ui); + ui.vertical_centered(|ui| { + View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + // Show tabs content. + self.tabs_ui(ui); + }); + }); }); // Show current node tab content. @@ -89,6 +94,7 @@ impl NetworkContent { ..Default::default() }) .show_inside(ui, |ui| { + // Draw node tab content. self.node_tab_content.ui(ui, frame, cb); }); }); @@ -148,7 +154,7 @@ impl NetworkContent { /// Draw tab buttons in the bottom of the screen. fn tabs_ui(&mut self, ui: &mut egui::Ui) { - ui.scope(|ui| { + ui.vertical_centered(|ui| { // Setup spacing between tabs. ui.style_mut().spacing.item_spacing = egui::vec2(4.0, 0.0); // Setup vertical padding inside tab button. @@ -213,7 +219,7 @@ impl NetworkContent { } }, |ui, frame| { if !Root::is_dual_panel_mode(ui) { - View::title_button(ui, CARDHOLDER, || { + View::title_button(ui, BRIEFCASE, || { Root::toggle_network_panel(); }); } diff --git a/src/gui/views/network/metrics.rs b/src/gui/views/network/metrics.rs index f184ae8..7453f3a 100644 --- a/src/gui/views/network/metrics.rs +++ b/src/gui/views/network/metrics.rs @@ -13,12 +13,12 @@ // limitations under the License. use egui::{RichText, Rounding, ScrollArea, vec2}; -use grin_servers::DiffBlock; +use grin_servers::{DiffBlock, ServerStats}; use crate::gui::Colors; use crate::gui::icons::{AT, COINS, CUBE_TRANSPARENT, HASH, HOURGLASS_LOW, HOURGLASS_MEDIUM, TIMER}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{NetworkContent, View}; +use crate::gui::views::{NetworkContent, Root, View}; use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; use crate::node::Node; @@ -56,91 +56,103 @@ impl NetworkTab for NetworkMetrics { return; } - let stats = server_stats.as_ref().unwrap(); - ui.add_space(1.0); - - // Show emission info. - View::sub_title(ui, format!("{} {}", COINS, t!("network_metrics.emission"))); - ui.columns(3, |columns| { - let supply = stats.header_stats.height as f64 * BLOCK_REWARD; - let rate = (YEARLY_SUPPLY * 100.0) / supply; - - columns[0].vertical_centered(|ui| { - View::rounded_box(ui, - format!("{}ツ", BLOCK_REWARD), - t!("network_metrics.reward"), - [true, false, true, false]); - }); - columns[1].vertical_centered(|ui| { - View::rounded_box(ui, - format!("{:.2}%", rate), - t!("network_metrics.inflation"), - [false, false, false, false]); - }); - columns[2].vertical_centered(|ui| { - View::rounded_box(ui, - supply.to_string(), - t!("network_metrics.supply"), - [false, true, false, true]); + ui.vertical_centered(|ui| { + View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + let stats = server_stats.as_ref().unwrap(); + // Show emission and difficulty info. + info_ui(ui, stats); + // Show difficulty adjustment window blocks. + blocks_ui(ui, stats); }); }); - ui.add_space(5.0); - - // Show difficulty adjustment window info. - let difficulty_title = t!( - "network_metrics.difficulty_window", - "size" => stats.diff_stats.window_size - ); - View::sub_title(ui, format!("{} {}", HOURGLASS_MEDIUM, difficulty_title)); - ui.columns(3, |columns| { - columns[0].vertical_centered(|ui| { - View::rounded_box(ui, - stats.diff_stats.height.to_string(), - t!("network_node.height"), - [true, false, true, false]); - }); - columns[1].vertical_centered(|ui| { - View::rounded_box(ui, - format!("{}s", stats.diff_stats.average_block_time), - t!("network_metrics.block_time"), - [false, false, false, false]); - }); - columns[2].vertical_centered(|ui| { - View::rounded_box(ui, - stats.diff_stats.average_difficulty.to_string(), - t!("network_node.difficulty"), - [false, true, false, true]); - }); - }); - ui.add_space(4.0); - - // Show difficulty adjustment window blocks. - let blocks_size = stats.diff_stats.last_blocks.len(); - ScrollArea::vertical() - .id_source("difficulty_scroll") - .auto_shrink([false; 2]) - .stick_to_bottom(true) - .show_rows( - ui, - BLOCK_ITEM_HEIGHT - 1.0, - blocks_size, - |ui, row_range| { - for index in row_range { - // Add space before the first item. - if index == 0 { - ui.add_space(4.0); - } - let db = stats.diff_stats.last_blocks.get(index).unwrap(); - block_item_ui(ui, db, View::item_rounding(index, blocks_size, false)); - } - }, - ); } } const BLOCK_ITEM_HEIGHT: f32 = 77.0; +/// Draw emission and difficulty info. +fn info_ui(ui: &mut egui::Ui, stats: &ServerStats) { + // Show emission info. + View::sub_title(ui, format!("{} {}", COINS, t!("network_metrics.emission"))); + ui.columns(3, |columns| { + let supply = stats.header_stats.height as f64 * BLOCK_REWARD; + let rate = (YEARLY_SUPPLY * 100.0) / supply; + + columns[0].vertical_centered(|ui| { + View::rounded_box(ui, + format!("{}ツ", BLOCK_REWARD), + t!("network_metrics.reward"), + [true, false, true, false]); + }); + columns[1].vertical_centered(|ui| { + View::rounded_box(ui, + format!("{:.2}%", rate), + t!("network_metrics.inflation"), + [false, false, false, false]); + }); + columns[2].vertical_centered(|ui| { + View::rounded_box(ui, + supply.to_string(), + t!("network_metrics.supply"), + [false, true, false, true]); + }); + }); + ui.add_space(5.0); + + // Show difficulty adjustment window info. + let difficulty_title = t!( + "network_metrics.difficulty_window", + "size" => stats.diff_stats.window_size + ); + View::sub_title(ui, format!("{} {}", HOURGLASS_MEDIUM, difficulty_title)); + ui.columns(3, |columns| { + columns[0].vertical_centered(|ui| { + View::rounded_box(ui, + stats.diff_stats.height.to_string(), + t!("network_node.height"), + [true, false, true, false]); + }); + columns[1].vertical_centered(|ui| { + View::rounded_box(ui, + format!("{}s", stats.diff_stats.average_block_time), + t!("network_metrics.block_time"), + [false, false, false, false]); + }); + columns[2].vertical_centered(|ui| { + View::rounded_box(ui, + stats.diff_stats.average_difficulty.to_string(), + t!("network_node.difficulty"), + [false, true, false, true]); + }); + }); +} + +/// Draw difficulty adjustment window blocks content. +fn blocks_ui(ui: &mut egui::Ui, stats: &ServerStats) { + let blocks_size = stats.diff_stats.last_blocks.len(); + ui.add_space(4.0); + ScrollArea::vertical() + .id_source("difficulty_scroll") + .auto_shrink([false; 2]) + .stick_to_bottom(true) + .show_rows( + ui, + BLOCK_ITEM_HEIGHT - 1.0, + blocks_size, + |ui, row_range| { + for index in row_range { + // Add space before the first item. + if index == 0 { + ui.add_space(4.0); + } + let db = stats.diff_stats.last_blocks.get(index).unwrap(); + block_item_ui(ui, db, View::item_rounding(index, blocks_size, false)); + } + }, + ); +} + /// Draw block difficulty item. fn block_item_ui(ui: &mut egui::Ui, db: &DiffBlock, rounding: Rounding) { let mut rect = ui.available_rect_before_wrap(); diff --git a/src/gui/views/network/mining.rs b/src/gui/views/network/mining.rs index eaa0f4f..812f6f3 100644 --- a/src/gui/views/network/mining.rs +++ b/src/gui/views/network/mining.rs @@ -19,7 +19,7 @@ use grin_servers::WorkerStats; use crate::gui::Colors; use crate::gui::icons::{BARBELL, CLOCK_AFTERNOON, CPU, CUBE, FADERS, FOLDER_DASHED, FOLDER_SIMPLE_MINUS, FOLDER_SIMPLE_PLUS, HARD_DRIVES, PLUGS, PLUGS_CONNECTED, POLYGON}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{NetworkContent, View}; +use crate::gui::views::{NetworkContent, Root, View}; use crate::gui::views::network::setup::StratumSetup; use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; use crate::node::{Node, NodeConfig}; @@ -73,7 +73,11 @@ impl NetworkTab for NetworkMining { .auto_shrink([false; 2]) .show(ui, |ui| { ui.add_space(1.0); - self.stratum_server_setup.ui(ui, frame, cb); + ui.vertical_centered(|ui| { + View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + self.stratum_server_setup.ui(ui, frame, cb); + }); + }); }); return; } diff --git a/src/gui/views/network/node.rs b/src/gui/views/network/node.rs index e99d929..7ed5659 100644 --- a/src/gui/views/network/node.rs +++ b/src/gui/views/network/node.rs @@ -18,7 +18,7 @@ use grin_servers::PeerStats; use crate::gui::Colors; use crate::gui::icons::{AT, CUBE, DEVICES, FLOW_ARROW, HANDSHAKE, PACKAGE, PLUGS_CONNECTED, SHARE_NETWORK}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{NetworkContent, View}; +use crate::gui::views::{NetworkContent, Root, View}; use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; use crate::node::Node; @@ -45,140 +45,150 @@ impl NetworkTab for NetworkNode { return; } - let stats = server_stats.as_ref().unwrap(); - ScrollArea::vertical() .id_source("integrated_node") .auto_shrink([false; 2]) .show(ui, |ui| { ui.add_space(2.0); - - // Show header info. - View::sub_title(ui, format!("{} {}", FLOW_ARROW, t!("network_node.header"))); - ui.columns(2, |columns| { - columns[0].vertical_centered(|ui| { - View::rounded_box(ui, - stats.header_stats.last_block_h.to_string(), - t!("network_node.hash"), - [true, false, false, false]); - }); - columns[1].vertical_centered(|ui| { - View::rounded_box(ui, - stats.header_stats.height.to_string(), - t!("network_node.height"), - [false, true, false, false]); + ui.vertical_centered(|ui| { + View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + // Show node stats content. + node_stats_ui(ui); }); }); - ui.columns(2, |columns| { - columns[0].vertical_centered(|ui| { - View::rounded_box(ui, - stats.header_stats.total_difficulty.to_string(), - t!("network_node.difficulty"), - [false, false, true, false]); - }); - columns[1].vertical_centered(|ui| { - let h_ts = stats.header_stats.latest_timestamp.timestamp(); - let h_time = View::format_time(h_ts); - View::rounded_box(ui, - h_time, - t!("network_node.time"), - [false, false, false, true]); - }); - }); - ui.add_space(5.0); - - // Show block info. - View::sub_title(ui, format!("{} {}", CUBE, t!("network_node.block"))); - ui.columns(2, |columns| { - columns[0].vertical_centered(|ui| { - View::rounded_box(ui, - stats.chain_stats.last_block_h.to_string(), - t!("network_node.hash"), - [true, false, false, false]); - }); - columns[1].vertical_centered(|ui| { - View::rounded_box(ui, - stats.chain_stats.height.to_string(), - t!("network_node.height"), - [false, true, false, false]); - }); - }); - ui.columns(2, |columns| { - columns[0].vertical_centered(|ui| { - View::rounded_box(ui, - stats.chain_stats.total_difficulty.to_string(), - t!("network_node.difficulty"), - [false, false, true, false]); - }); - columns[1].vertical_centered(|ui| { - let b_ts = stats.chain_stats.latest_timestamp.timestamp(); - let b_time = View::format_time(b_ts); - View::rounded_box(ui, - b_time, - t!("network_node.time"), - [false, false, false, true]); - }); - }); - ui.add_space(5.0); - - // Show data info. - View::sub_title(ui, format!("{} {}", SHARE_NETWORK, t!("network_node.data"))); - ui.columns(2, |columns| { - columns[0].vertical_centered(|ui| { - let tx_stat = match &stats.tx_stats { - None => "0 (0)".to_string(), - Some(tx) => format!("{} ({})", tx.tx_pool_size, tx.tx_pool_kernels) - }; - View::rounded_box(ui, - tx_stat, - t!("network_node.main_pool"), - [true, false, false, false]); - }); - columns[1].vertical_centered(|ui| { - let stem_tx_stat = match &stats.tx_stats { - None => "0 (0)".to_string(), - Some(stx) => format!("{} ({})", - stx.stem_pool_size, - stx.stem_pool_kernels) - }; - View::rounded_box(ui, - stem_tx_stat, - t!("network_node.stem_pool"), - [false, true, false, false]); - }); - }); - ui.columns(2, |columns| { - columns[0].vertical_centered(|ui| { - View::rounded_box(ui, - stats.disk_usage_gb.to_string(), - t!("network_node.size"), - [false, false, true, false]); - }); - columns[1].vertical_centered(|ui| { - View::rounded_box(ui, - stats.peer_count.to_string(), - t!("network_node.peers"), - [false, false, false, true]); - }); - }); - ui.add_space(5.0); - - // Show peer stats when available. - if stats.peer_count > 0 { - View::sub_title(ui, format!("{} {}", HANDSHAKE, t!("network_node.peers"))); - let peers = &stats.peer_stats; - for (index, ps) in peers.iter().enumerate() { - peer_item_ui(ui, ps, View::item_rounding(index, peers.len(), false)); - // Add space after the last item. - if index == peers.len() - 1 { - ui.add_space(5.0); - } - } - } }); } } +/// Draw node statistics content. +fn node_stats_ui(ui: &mut egui::Ui) { + let server_stats = Node::get_stats(); + let stats = server_stats.as_ref().unwrap(); + + // Show header info. + View::sub_title(ui, format!("{} {}", FLOW_ARROW, t!("network_node.header"))); + ui.columns(2, |columns| { + columns[0].vertical_centered(|ui| { + View::rounded_box(ui, + stats.header_stats.last_block_h.to_string(), + t!("network_node.hash"), + [true, false, false, false]); + }); + columns[1].vertical_centered(|ui| { + View::rounded_box(ui, + stats.header_stats.height.to_string(), + t!("network_node.height"), + [false, true, false, false]); + }); + }); + ui.columns(2, |columns| { + columns[0].vertical_centered(|ui| { + View::rounded_box(ui, + stats.header_stats.total_difficulty.to_string(), + t!("network_node.difficulty"), + [false, false, true, false]); + }); + columns[1].vertical_centered(|ui| { + let h_ts = stats.header_stats.latest_timestamp.timestamp(); + let h_time = View::format_time(h_ts); + View::rounded_box(ui, + h_time, + t!("network_node.time"), + [false, false, false, true]); + }); + }); + ui.add_space(5.0); + + // Show block info. + View::sub_title(ui, format!("{} {}", CUBE, t!("network_node.block"))); + ui.columns(2, |columns| { + columns[0].vertical_centered(|ui| { + View::rounded_box(ui, + stats.chain_stats.last_block_h.to_string(), + t!("network_node.hash"), + [true, false, false, false]); + }); + columns[1].vertical_centered(|ui| { + View::rounded_box(ui, + stats.chain_stats.height.to_string(), + t!("network_node.height"), + [false, true, false, false]); + }); + }); + ui.columns(2, |columns| { + columns[0].vertical_centered(|ui| { + View::rounded_box(ui, + stats.chain_stats.total_difficulty.to_string(), + t!("network_node.difficulty"), + [false, false, true, false]); + }); + columns[1].vertical_centered(|ui| { + let b_ts = stats.chain_stats.latest_timestamp.timestamp(); + let b_time = View::format_time(b_ts); + View::rounded_box(ui, + b_time, + t!("network_node.time"), + [false, false, false, true]); + }); + }); + ui.add_space(5.0); + + // Show data info. + View::sub_title(ui, format!("{} {}", SHARE_NETWORK, t!("network_node.data"))); + ui.columns(2, |columns| { + columns[0].vertical_centered(|ui| { + let tx_stat = match &stats.tx_stats { + None => "0 (0)".to_string(), + Some(tx) => format!("{} ({})", tx.tx_pool_size, tx.tx_pool_kernels) + }; + View::rounded_box(ui, + tx_stat, + t!("network_node.main_pool"), + [true, false, false, false]); + }); + columns[1].vertical_centered(|ui| { + let stem_tx_stat = match &stats.tx_stats { + None => "0 (0)".to_string(), + Some(stx) => format!("{} ({})", + stx.stem_pool_size, + stx.stem_pool_kernels) + }; + View::rounded_box(ui, + stem_tx_stat, + t!("network_node.stem_pool"), + [false, true, false, false]); + }); + }); + ui.columns(2, |columns| { + columns[0].vertical_centered(|ui| { + View::rounded_box(ui, + stats.disk_usage_gb.to_string(), + t!("network_node.size"), + [false, false, true, false]); + }); + columns[1].vertical_centered(|ui| { + View::rounded_box(ui, + stats.peer_count.to_string(), + t!("network_node.peers"), + [false, false, false, true]); + }); + }); + ui.add_space(5.0); + + // Show peer stats when available. + if stats.peer_count > 0 { + View::sub_title(ui, format!("{} {}", HANDSHAKE, t!("network_node.peers"))); + let peers = &stats.peer_stats; + for (index, ps) in peers.iter().enumerate() { + peer_item_ui(ui, ps, View::item_rounding(index, peers.len(), false)); + // Add space after the last item. + if index == peers.len() - 1 { + ui.add_space(5.0); + } + } + } +} + /// Draw connected peer info item. fn peer_item_ui(ui: &mut egui::Ui, peer: &PeerStats, rounding: Rounding) { let mut rect = ui.available_rect_before_wrap(); diff --git a/src/gui/views/network/settings.rs b/src/gui/views/network/settings.rs index 13c8403..01cd485 100644 --- a/src/gui/views/network/settings.rs +++ b/src/gui/views/network/settings.rs @@ -17,7 +17,7 @@ use egui::{RichText, ScrollArea}; use crate::gui::Colors; use crate::gui::icons::ARROW_COUNTER_CLOCKWISE; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, View}; +use crate::gui::views::{Modal, Root, View}; use crate::gui::views::network::setup::{DandelionSetup, NodeSetup, P2PSetup, PoolSetup, StratumSetup}; use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; use crate::gui::views::types::{ModalContainer, ModalPosition}; @@ -89,44 +89,47 @@ impl NetworkTab for NetworkSettings { .auto_shrink([false; 2]) .show(ui, |ui| { ui.add_space(1.0); + ui.vertical_centered(|ui| { + View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + // Draw node setup section. + self.node.ui(ui, frame, cb); - // Draw node setup section. - self.node.ui(ui, frame, cb); + ui.add_space(6.0); + View::horizontal_line(ui, Colors::STROKE); + ui.add_space(4.0); - ui.add_space(6.0); - View::horizontal_line(ui, Colors::STROKE); - ui.add_space(4.0); + // Draw P2P server setup section. + self.p2p.ui(ui, frame, cb); - // Draw P2P server setup section. - self.p2p.ui(ui, frame, cb); + ui.add_space(6.0); + View::horizontal_line(ui, Colors::STROKE); + ui.add_space(4.0); - ui.add_space(6.0); - View::horizontal_line(ui, Colors::STROKE); - ui.add_space(4.0); + // Draw Stratum server setup section. + self.stratum.ui(ui, frame, cb); - // Draw Stratum server setup section. - self.stratum.ui(ui, frame, cb); + ui.add_space(6.0); + View::horizontal_line(ui, Colors::STROKE); + ui.add_space(4.0); - ui.add_space(6.0); - View::horizontal_line(ui, Colors::STROKE); - ui.add_space(4.0); + // Draw pool setup section. + self.pool.ui(ui, frame, cb); - // Draw pool setup section. - self.pool.ui(ui, frame, cb); + ui.add_space(6.0); + View::horizontal_line(ui, Colors::STROKE); + ui.add_space(4.0); - ui.add_space(6.0); - View::horizontal_line(ui, Colors::STROKE); - ui.add_space(4.0); + // Draw Dandelion server setup section. + self.dandelion.ui(ui, frame, cb); - // Draw Dandelion server setup section. - self.dandelion.ui(ui, frame, cb); + ui.add_space(6.0); + View::horizontal_line(ui, Colors::STROKE); + ui.add_space(6.0); - ui.add_space(6.0); - View::horizontal_line(ui, Colors::STROKE); - ui.add_space(6.0); - - // Draw reset settings content. - reset_settings_ui(ui); + // Draw reset settings content. + reset_settings_ui(ui); + }); + }); }); } } -- 2.39.5 From e8a0be076fe8f4ab94aed152ff8cbc36ae98587c Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 22 Apr 2024 17:44:41 +0300 Subject: [PATCH 20/46] ui: node loading compact translation --- locales/en.yml | 2 +- locales/ru.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/en.yml b/locales/en.yml index 1fdeb8a..402b375 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -44,7 +44,7 @@ wallets: locked: Locked unlocked: Unlocked enable_node: 'Enable integrated node to use the wallet or change connection settings by selecting %{settings} at the bottom of the screen.' - node_loading: 'Wallet will be loaded after integrated node synchronization, you can change connection settings by selecting %{settings} at the bottom of the screen.' + node_loading: 'Wallet will be loaded after integrated node synchronization, you can change connection by selecting %{settings} at the bottom of the screen.' loading: Loading closing: Closing checking: Checking diff --git a/locales/ru.yml b/locales/ru.yml index 6fb99e3..a191118 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -44,7 +44,7 @@ wallets: locked: Заблокирован unlocked: Разблокирован enable_node: 'Чтобы использовать кошелёк, включите встроенный узел или измените настройки подключения, выбрав %{settings} внизу экрана.' - node_loading: 'Кошелёк будет загружен после синхронизации встроенного узла, вы можете изменить настройки подключения, выбрав %{settings} внизу экрана.' + node_loading: 'Кошелёк будет загружен после синхронизации встроенного узла, вы можете изменить подключение, выбрав %{settings} внизу экрана.' loading: Загружается closing: Закрывается checking: Проверяется -- 2.39.5 From faf47125a59a3a141b536064d9af2469f6030e03 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 22 Apr 2024 21:55:34 +0300 Subject: [PATCH 21/46] fix: check for empty amount at request creation --- src/gui/views/wallets/wallet/messages.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/views/wallets/wallet/messages.rs b/src/gui/views/wallets/wallet/messages.rs index fe6dbfb..3000cd4 100644 --- a/src/gui/views/wallets/wallet/messages.rs +++ b/src/gui/views/wallets/wallet/messages.rs @@ -638,6 +638,9 @@ impl WalletMessages { columns[1].vertical_centered_justified(|ui| { // Button to create Slatepack message for request. View::button(ui, t!("continue"), Colors::WHITE, || { + if self.amount_edit.is_empty() { + return; + } if let Ok(a) = amount_from_hr_string(self.amount_edit.as_str()) { let message = if self.send_request { wallet.send(a) -- 2.39.5 From b3f0addf3ebc88e9eb58e4bcb22a5c8d9816782c Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 22 Apr 2024 21:59:26 +0300 Subject: [PATCH 22/46] fix: txs info width --- src/gui/views/wallets/wallet/txs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/views/wallets/wallet/txs.rs b/src/gui/views/wallets/wallet/txs.rs index d2452a6..3575542 100644 --- a/src/gui/views/wallets/wallet/txs.rs +++ b/src/gui/views/wallets/wallet/txs.rs @@ -74,7 +74,7 @@ impl WalletInfo { let txs_size = data.txs.len(); // Show transactions info. - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.35, |ui| { + View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.25, |ui| { let amount_awaiting_conf = data.info.amount_awaiting_confirmation; let amount_awaiting_fin = data.info.amount_awaiting_finalization; let amount_locked = data.info.amount_locked; -- 2.39.5 From f22050795ef61e341b29cee61c17f966df75a4a2 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 22 Apr 2024 22:01:09 +0300 Subject: [PATCH 23/46] ui: network type translation --- locales/en.yml | 2 ++ locales/ru.yml | 4 +++- src/gui/views/network/setup/node.rs | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/locales/en.yml b/locales/en.yml index 402b375..0734a36 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -105,6 +105,8 @@ wallets: network: self: Network type: 'Network type:' + mainnet: Main + testnet: Test connections: Connections node: Integrated node metrics: Metrics diff --git a/locales/ru.yml b/locales/ru.yml index a191118..2691186 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -67,7 +67,7 @@ wallets: txs: Транзакции messages: Сообщения transport: Транспорт - input_slatepack_desc: 'Введите сообщение для создания ответа или завершения транзакции:' + input_slatepack_desc: 'Введите полученное сообщение для создания ответа или завершения транзакции:' send_slatepack_desc: 'Отправьте сообщение получателю средств для завершения транзакции:' parse_slatepack_err: 'Во время обработки сообщения произошла ошибка, проверьте входные данные:' pay_balance_error: 'Средств на аккаунте недостаточно для оплаты %{amount} ツ и комиссии сети.' @@ -105,6 +105,8 @@ wallets: network: self: Сеть type: 'Тип сети:' + mainnet: Основная + testnet: Тестовая connections: Подключения node: Встроенный узел metrics: Показатели diff --git a/src/gui/views/network/setup/node.rs b/src/gui/views/network/setup/node.rs index 05ff87c..be92ce3 100644 --- a/src/gui/views/network/setup/node.rs +++ b/src/gui/views/network/setup/node.rs @@ -232,11 +232,11 @@ impl NodeSetup { ui.columns(2, |columns| { columns[0].vertical_centered(|ui| { let main_type = ChainTypes::Mainnet; - View::radio_value(ui, &mut selected_chain_type, main_type, "Mainnet".to_string()); + View::radio_value(ui, &mut selected_chain_type, main_type, t!("network.mainnet")); }); columns[1].vertical_centered(|ui| { let test_type = ChainTypes::Testnet; - View::radio_value(ui, &mut selected_chain_type, test_type, "Testnet".to_string()); + View::radio_value(ui, &mut selected_chain_type, test_type, t!("network.testnet")); }) }); ui.add_space(8.0); -- 2.39.5 From df4500fac6752a42e81087f2c22b800ce615087b Mon Sep 17 00:00:00 2001 From: ardocrat Date: Mon, 22 Apr 2024 23:32:29 +0300 Subject: [PATCH 24/46] ui: txs, account paddings fixes --- src/gui/views/wallets/wallet/content.rs | 2 +- src/gui/views/wallets/wallet/txs.rs | 30 ++++++++++++++++++++----- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index 0c99146..984461d 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -95,7 +95,7 @@ impl WalletContent { .show_animated_inside(ui, show_balance, |ui| { ui.vertical_centered(|ui| { // Draw account info. - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.25, |ui| { + View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { self.account_ui(ui, wallet, data.unwrap(), cb); }); }); diff --git a/src/gui/views/wallets/wallet/txs.rs b/src/gui/views/wallets/wallet/txs.rs index 3575542..d5bc8ae 100644 --- a/src/gui/views/wallets/wallet/txs.rs +++ b/src/gui/views/wallets/wallet/txs.rs @@ -61,7 +61,8 @@ impl WalletTab for WalletInfo { }) .show_inside(ui, |ui| { ui.vertical_centered(|ui| { - self.txs_ui(ui, wallet); + let data = wallet.get_data().unwrap(); + self.txs_ui(ui, wallet, &data); }); }); } @@ -69,12 +70,11 @@ impl WalletTab for WalletInfo { impl WalletInfo { /// Draw transactions content. - fn txs_ui(&self, ui: &mut egui::Ui, wallet: &mut Wallet) { - let data = wallet.get_data().unwrap(); + fn txs_ui(&self, ui: &mut egui::Ui, wallet: &mut Wallet, data: &WalletData) { let txs_size = data.txs.len(); // Show transactions info. - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.25, |ui| { + View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { let amount_awaiting_conf = data.info.amount_awaiting_confirmation; let amount_awaiting_fin = data.info.amount_awaiting_finalization; let amount_locked = data.info.amount_locked; @@ -140,12 +140,17 @@ impl WalletInfo { .show_rows(ui, TX_ITEM_HEIGHT, txs_size, |ui, row_range| { ui.add_space(3.0); View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + let amount_awaiting_conf = data.info.amount_awaiting_confirmation; + let amount_awaiting_fin = data.info.amount_awaiting_finalization; + let amount_locked = data.info.amount_locked; + let extra_padding = amount_awaiting_conf != 0 || amount_awaiting_fin != 0 || + amount_locked != 0; for index in row_range { let tx = data.txs.get(index).unwrap(); // Setup item rounding. let item_rounding = View::item_rounding(index, txs_size, false); // Show transaction item. - tx_item_ui(ui, tx, item_rounding, &data, wallet); + tx_item_ui(ui, tx, item_rounding, extra_padding, &data, wallet); } }); }); @@ -159,10 +164,15 @@ const TX_ITEM_HEIGHT: f32 = 76.0; fn tx_item_ui(ui: &mut egui::Ui, tx: &WalletTransaction, mut rounding: Rounding, + extra_padding: bool, data: &WalletData, wallet: &mut Wallet) { // Setup layout size. let mut rect = ui.available_rect_before_wrap(); + if extra_padding { + rect.min += egui::emath::vec2(6.0, 0.0); + rect.max -= egui::emath::vec2(6.0, 0.0); + } rect.set_height(TX_ITEM_HEIGHT); // Draw round background. @@ -171,6 +181,10 @@ fn tx_item_ui(ui: &mut egui::Ui, ui.vertical(|ui| { ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { + if extra_padding { + ui.add_space(-6.0); + } + // Draw button to show transaction info. rounding.nw = 0.0; rounding.sw = 0.0; @@ -217,7 +231,11 @@ fn tx_item_ui(ui: &mut egui::Ui, let layout_size = ui.available_size(); ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { - ui.add_space(8.0); + if extra_padding { + ui.add_space(12.0); + } else { + ui.add_space(6.0); + } ui.vertical(|ui| { ui.add_space(3.0); -- 2.39.5 From 92e1da511d455b088940e2d775451add5fafeb96 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Wed, 24 Apr 2024 01:09:27 +0300 Subject: [PATCH 25/46] modal: fix min width --- src/gui/views/modal.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/views/modal.rs b/src/gui/views/modal.rs index cb1d0a2..d83d80f 100644 --- a/src/gui/views/modal.rs +++ b/src/gui/views/modal.rs @@ -180,6 +180,7 @@ impl Modal { .title_bar(false) .resizable(false) .collapsible(false) + .min_width(width) .default_width(width) .anchor(content_align, content_offset) .frame(egui::Frame { -- 2.39.5 From 01b5b21488ebcdc2f936c34cb236e60dea4dfe7e Mon Sep 17 00:00:00 2001 From: ardocrat Date: Wed, 24 Apr 2024 01:42:56 +0300 Subject: [PATCH 26/46] wallet: fix for already canceled invoice, transaction info modal, ability to finalize from list --- Cargo.lock | 12 +- Cargo.toml | 10 +- locales/en.yml | 9 +- locales/ru.yml | 11 +- src/gui/views/wallets/wallet/content.rs | 6 +- src/gui/views/wallets/wallet/messages.rs | 28 +- src/gui/views/wallets/wallet/mod.rs | 2 +- src/gui/views/wallets/wallet/txs.rs | 518 +++++++++++++++++++---- src/wallet/types.rs | 21 +- src/wallet/wallet.rs | 131 ++++-- 10 files changed, 591 insertions(+), 157 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be20ab4..61d95ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2808,7 +2808,7 @@ dependencies = [ [[package]] name = "grin_wallet_api" version = "5.2.0-beta.1" -source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#75363a9a258bc1fb0cf60bfb4c88a8a653b122f2" +source = "git+https://github.com/yeastplume/grin-wallet?branch=prevent_double_pay#6e6b16a61c53825447f27ad49ba654c922cf9702" dependencies = [ "base64 0.12.3", "chrono", @@ -2833,7 +2833,7 @@ dependencies = [ [[package]] name = "grin_wallet_config" version = "5.2.0-beta.1" -source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#75363a9a258bc1fb0cf60bfb4c88a8a653b122f2" +source = "git+https://github.com/yeastplume/grin-wallet?branch=prevent_double_pay#6e6b16a61c53825447f27ad49ba654c922cf9702" dependencies = [ "dirs 2.0.2", "grin_core", @@ -2848,7 +2848,7 @@ dependencies = [ [[package]] name = "grin_wallet_controller" version = "5.2.0-beta.1" -source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#75363a9a258bc1fb0cf60bfb4c88a8a653b122f2" +source = "git+https://github.com/yeastplume/grin-wallet?branch=prevent_double_pay#6e6b16a61c53825447f27ad49ba654c922cf9702" dependencies = [ "chrono", "easy-jsonrpc-mw", @@ -2882,7 +2882,7 @@ dependencies = [ [[package]] name = "grin_wallet_impls" version = "5.2.0-beta.1" -source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#75363a9a258bc1fb0cf60bfb4c88a8a653b122f2" +source = "git+https://github.com/yeastplume/grin-wallet?branch=prevent_double_pay#6e6b16a61c53825447f27ad49ba654c922cf9702" dependencies = [ "base64 0.12.3", "blake2-rfc", @@ -2921,7 +2921,7 @@ dependencies = [ [[package]] name = "grin_wallet_libwallet" version = "5.2.0-beta.1" -source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#75363a9a258bc1fb0cf60bfb4c88a8a653b122f2" +source = "git+https://github.com/yeastplume/grin-wallet?branch=prevent_double_pay#6e6b16a61c53825447f27ad49ba654c922cf9702" dependencies = [ "age", "base64 0.9.3", @@ -2958,7 +2958,7 @@ dependencies = [ [[package]] name = "grin_wallet_util" version = "5.2.0-beta.1" -source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#75363a9a258bc1fb0cf60bfb4c88a8a653b122f2" +source = "git+https://github.com/yeastplume/grin-wallet?branch=prevent_double_pay#6e6b16a61c53825447f27ad49ba654c922cf9702" dependencies = [ "data-encoding", "ed25519-dalek", diff --git a/Cargo.toml b/Cargo.toml index 959819b..73a0d0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,11 +26,11 @@ grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "master grin_util = { git = "https://github.com/mimblewimble/grin", branch = "master" } ## wallet -grin_wallet_impls = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } -grin_wallet_api = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } -grin_wallet_libwallet = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } -grin_wallet_util = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } -grin_wallet_controller = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } +grin_wallet_impls = { git = "https://github.com/yeastplume/grin-wallet", branch = "prevent_double_pay" } +grin_wallet_api = { git = "https://github.com/yeastplume/grin-wallet", branch = "prevent_double_pay" } +grin_wallet_libwallet = { git = "https://github.com/yeastplume/grin-wallet", branch = "prevent_double_pay" } +grin_wallet_util = { git = "https://github.com/yeastplume/grin-wallet", branch = "prevent_double_pay" } +grin_wallet_controller = { git = "https://github.com/yeastplume/grin-wallet", branch = "prevent_double_pay" } ## ui egui = { version = "0.27.2", default-features = false } diff --git a/locales/en.yml b/locales/en.yml index 0734a36..7ad1288 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -10,6 +10,8 @@ show: Show delete: Delete clear: Clear create: Create +id: Identifier +kernel: Kernel wallets: await_conf_amount: Awaiting confirmation await_fin_amount: Awaiting finalization @@ -65,11 +67,11 @@ wallets: tx_finalizing: Finalizing tx_confirmed: Confirmed txs: Transactions + input_finalize_desc: 'Enter message to finalize the transaction:' messages: Messages transport: Transport input_slatepack_desc: 'Enter message to create response or finalize the transaction:' - send_slatepack_desc: 'Send message to receiver of funds to finalize the transaction:' - parse_slatepack_err: 'An error occurred during handling of the message, check input data:' + parse_slatepack_err: 'An error occurred during reading of the message, check input data:' pay_balance_error: 'Account balance is insufficient to pay %{amount} ツ and network fee.' parse_i1_slatepack_desc: 'To pay %{amount} ツ send this message to the receiver:' parse_i2_slatepack_desc: 'Finalize transaction to receive %{amount} ツ' @@ -78,7 +80,8 @@ wallets: parse_s2_slatepack_desc: 'Finalize transaction to send %{amount} ツ' parse_s3_slatepack_desc: 'Post transaction to finalize sending of %{amount} ツ' resp_slatepack_err: 'An error occurred during creation of the response, check input data:' - resp_exists_err: 'Such transaction already exists.' + resp_exists_err: Such transaction already exists. + resp_canceled_err: Such transaction was already canceled. create_request_desc: 'Create request to send or receive the funds:' send_request_desc: 'You have created a request to send %{amount} ツ. Send this message to the receiver:' send_slatepack_err: An error occurred during creation of request to send funds, check input data. diff --git a/locales/ru.yml b/locales/ru.yml index 2691186..cc05e2a 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -10,6 +10,8 @@ show: Показать delete: Удалить clear: Очистить create: Создать +id: Идентификатор +kernel: Ядро wallets: await_conf_amount: Ожидает подтверждения await_fin_amount: Ожидает завершения @@ -65,11 +67,11 @@ wallets: tx_finalizing: Завершение tx_confirmed: Подтверждено txs: Транзакции + input_finalize_desc: 'Введите полученное сообщение для завершения транзакции:' messages: Сообщения transport: Транспорт input_slatepack_desc: 'Введите полученное сообщение для создания ответа или завершения транзакции:' - send_slatepack_desc: 'Отправьте сообщение получателю средств для завершения транзакции:' - parse_slatepack_err: 'Во время обработки сообщения произошла ошибка, проверьте входные данные:' + parse_slatepack_err: 'Во время чтения сообщения произошла ошибка, проверьте входные данные:' pay_balance_error: 'Средств на аккаунте недостаточно для оплаты %{amount} ツ и комиссии сети.' parse_i1_slatepack_desc: 'Для оплаты %{amount} ツ отправьте это сообщение получателю:' parse_i2_slatepack_desc: 'Завершите транзакцию для получения %{amount} ツ' @@ -78,8 +80,9 @@ wallets: parse_s2_slatepack_desc: 'Завершите транзакцию для отправки %{amount} ツ' parse_s3_slatepack_desc: 'Опубликуйте транзакцию для завершения отправки %{amount} ツ' resp_slatepack_err: 'Во время создания ответа произошла ошибка, проверьте входные данные:' - resp_exists_err: 'Такая транзакция уже существует.' - create_request_desc: 'Cоздать запрос на отправку или получение средств:' + resp_exists_err: Такая транзакция уже существует. + resp_canceled_err: Такая транзакция уже была отменена. + create_request_desc: 'Создайте запрос на отправку или получение средств:' send_request_desc: 'Вы создали запрос на отправку %{amount} ツ. Отправьте это сообщение получателю:' send_slatepack_err: Во время создания запроса на отправку средств произошла ошибка, проверьте входные данные. invoice_desc: 'Вы создали запрос на получение %{amount} ツ. Отправьте это сообщение отправителю:' diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index 984461d..a875d1e 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -23,7 +23,7 @@ use crate::gui::icons::{BRIDGE, CHAT_CIRCLE_TEXT, CHECK, CHECK_FAT, FOLDER_USER, use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, Root, View}; use crate::gui::views::types::{ModalPosition, TextEditOptions}; -use crate::gui::views::wallets::{WalletInfo, WalletMessages, WalletTransport, WalletSettings}; +use crate::gui::views::wallets::{WalletTransactions, WalletMessages, WalletTransport, WalletSettings}; use crate::gui::views::wallets::types::{GRIN, WalletTab, WalletTabType}; use crate::node::Node; use crate::wallet::{Wallet, WalletConfig}; @@ -52,7 +52,7 @@ impl Default for WalletContent { account_creating: false, account_label_edit: "".to_string(), account_creation_error: false, - current_tab: Box::new(WalletInfo::default()) + current_tab: Box::new(WalletTransactions::default()) } } } @@ -349,7 +349,7 @@ impl WalletContent { ui.columns(4, |columns| { columns[0].vertical_centered_justified(|ui| { View::tab_button(ui, GRAPH, current_type == WalletTabType::Txs, || { - self.current_tab = Box::new(WalletInfo::default()); + self.current_tab = Box::new(WalletTransactions::default()); }); }); columns[1].vertical_centered_justified(|ui| { diff --git a/src/gui/views/wallets/wallet/messages.rs b/src/gui/views/wallets/wallet/messages.rs index 3000cd4..f67bf27 100644 --- a/src/gui/views/wallets/wallet/messages.rs +++ b/src/gui/views/wallets/wallet/messages.rs @@ -146,14 +146,14 @@ impl WalletMessages { ui: &mut egui::Ui, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) { - ui.add_space(4.0); + ui.add_space(3.0); // Show creation of request to send or receive funds. self.request_ui(ui, cb); ui.add_space(12.0); View::horizontal_line(ui, Colors::ITEM_STROKE); - ui.add_space(8.0); + ui.add_space(6.0); // Show Slatepack message input field. self.input_slatepack_ui(ui, wallet, cb); @@ -326,11 +326,10 @@ impl WalletMessages { } else { show_dandelion = true; View::button(ui, t!("wallets.finalize"), Colors::GOLD, || { - let message = self.message_edit.clone(); let slate = self.message_slate.clone().unwrap(); if slate.state == SlateState::Invoice3 || slate.state == SlateState::Standard3 { - if let Ok(_) = wallet.post(&slate, self.dandelion) { + if wallet.post(&slate, self.dandelion).is_ok() { self.message_edit.clear(); self.message_slate = None; } else { @@ -341,7 +340,8 @@ impl WalletMessages { ); } } else { - if let Ok(_) = wallet.finalize(message, self.dandelion) { + let r = wallet.finalize(&self.message_edit, self.dandelion); + if r.is_ok() { self.message_edit.clear(); self.message_slate = None; } else { @@ -397,7 +397,7 @@ impl WalletMessages { if self.message_edit.is_empty() { return; } - if let Ok(mut slate) = wallet.parse_slatepack(self.message_edit.clone()) { + if let Ok(mut slate) = wallet.parse_slatepack(&self.message_edit) { println!("parse_message: {}", slate); // Try to setup empty amount from transaction by id. @@ -423,13 +423,23 @@ impl WalletMessages { match slate.state { SlateState::Standard1 | SlateState::Invoice1 => { let resp = if slate.state == SlateState::Standard1 { - wallet.receive(self.message_edit.clone()) + wallet.receive(&self.message_edit) } else { - wallet.pay(self.message_edit.clone()) + wallet.pay(&self.message_edit) }; if resp.is_ok() { self.response_edit = resp.unwrap(); } else { + match resp.err().unwrap() { + grin_wallet_libwallet::Error::TransactionWasCancelled {..} => { + // Set already canceled transaction error message. + self.message_error = Some( + MessageError::Response(t!("wallets.resp_canceled_err")) + ); + return; + } + _ => {} + } // Check if tx with same slate id already exists. let exists_tx = wallet.tx_by_slate(&slate).is_some(); if exists_tx { @@ -726,7 +736,7 @@ impl WalletMessages { // Button to cancel transaction. let cancel = format!("{} {}", PROHIBIT, t!("modal.cancel")); View::colored_text_button(ui, cancel, Colors::RED, Colors::BUTTON, || { - if let Ok(slate) = wallet.parse_slatepack(self.request_edit.clone()) { + if let Ok(slate) = wallet.parse_slatepack(&self.request_edit) { if let Some(tx) = wallet.tx_by_slate(&slate) { wallet.cancel(tx.data.id); } diff --git a/src/gui/views/wallets/wallet/mod.rs b/src/gui/views/wallets/wallet/mod.rs index 6db373d..0c93bc9 100644 --- a/src/gui/views/wallets/wallet/mod.rs +++ b/src/gui/views/wallets/wallet/mod.rs @@ -15,7 +15,7 @@ pub mod types; mod txs; -pub use txs::WalletInfo; +pub use txs::WalletTransactions; mod messages; pub use messages::WalletMessages; diff --git a/src/gui/views/wallets/wallet/txs.rs b/src/gui/views/wallets/wallet/txs.rs index d5bc8ae..2ff0dd5 100644 --- a/src/gui/views/wallets/wallet/txs.rs +++ b/src/gui/views/wallets/wallet/txs.rs @@ -15,24 +15,50 @@ use egui::{Align, Id, Layout, Margin, RichText, Rounding, ScrollArea}; use egui::scroll_area::ScrollBarVisibility; use grin_core::core::amount_to_hr_string; +use grin_util::ToHex; use grin_wallet_libwallet::{Slate, SlateState, TxLogEntryType}; use crate::gui::Colors; -use crate::gui::icons::{ARROW_CIRCLE_DOWN, ARROW_CIRCLE_UP, ARROW_CLOCKWISE, ARROWS_CLOCKWISE, 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, ARROW_CIRCLE_UP, ARROW_CLOCKWISE, BRIDGE, CALENDAR_CHECK, CHAT_CIRCLE_TEXT, CHECK, CHECK_CIRCLE, CLIPBOARD_TEXT, COPY, DOTS_THREE_CIRCLE, FILE_ARCHIVE, FILE_TEXT, GEAR_FINE, HASH_STRAIGHT, PROHIBIT, X_CIRCLE}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Root, View}; +use crate::gui::views::{Modal, Root, View}; +use crate::gui::views::types::ModalPosition; use crate::gui::views::wallets::types::WalletTab; -use crate::gui::views::wallets::wallet::types::{GRIN, WalletTabType}; +use crate::gui::views::wallets::wallet::types::{GRIN, SLATEPACK_MESSAGE_HINT, WalletTabType}; use crate::gui::views::wallets::wallet::WalletContent; use crate::wallet::types::{WalletData, WalletTransaction}; use crate::wallet::Wallet; +/// Wallet transactions tab content. +pub struct WalletTransactions { + /// Transaction identifier to user at [`Modal`]. + tx_info_id: Option, + /// Transaction [`Slate`] to use at [`Modal`]. + tx_info_slate: Option, + /// Response Slatepack message input value at [`Modal`]. + tx_info_response_edit: String, + /// Finalization Slatepack message input value at [`Modal`]. + tx_info_finalize_edit: String, + /// Flag to check if error happened during transaction finalization at [`Modal`]. + tx_info_finalize_error: bool, + /// Flag to check if tx finalization requested at [`Modal`]. + tx_info_finalize: bool, +} -/// Wallet info tab content. -#[derive(Default)] -pub struct WalletInfo; +impl Default for WalletTransactions { + fn default() -> Self { + Self { + tx_info_id: None, + tx_info_slate: None, + tx_info_response_edit: "".to_string(), + tx_info_finalize_edit: "".to_string(), + tx_info_finalize_error: false, + tx_info_finalize: false, + } + } +} -impl WalletTab for WalletInfo { +impl WalletTab for WalletTransactions { fn get_type(&self) -> WalletTabType { WalletTabType::Txs } @@ -41,11 +67,14 @@ impl WalletTab for WalletInfo { ui: &mut egui::Ui, _: &mut eframe::Frame, wallet: &mut Wallet, - _: &dyn PlatformCallbacks) { + cb: &dyn PlatformCallbacks) { if WalletContent::sync_ui(ui, wallet) { return; } + // Show modal content for this ui container. + self.modal_content_ui(ui, wallet, cb); + // Show wallet transactions panel. egui::CentralPanel::default() .frame(egui::Frame { @@ -62,22 +91,31 @@ impl WalletTab for WalletInfo { .show_inside(ui, |ui| { ui.vertical_centered(|ui| { let data = wallet.get_data().unwrap(); - self.txs_ui(ui, wallet, &data); + self.txs_ui(ui, wallet, &data, cb); }); }); } } -impl WalletInfo { +/// Identifier for transaction information [`Modal`]. +const TX_INFO_MODAL: &'static str = "tx_info_modal"; + +/// Height of transaction list item. +const TX_ITEM_HEIGHT: f32 = 76.0; + +impl WalletTransactions { /// Draw transactions content. - fn txs_ui(&self, ui: &mut egui::Ui, wallet: &mut Wallet, data: &WalletData) { - let txs_size = data.txs.len(); + fn txs_ui(&mut self, + ui: &mut egui::Ui, + wallet: &mut Wallet, + data: &WalletData, + cb: &dyn PlatformCallbacks) { + let amount_awaiting_conf = data.info.amount_awaiting_confirmation; + let amount_awaiting_fin = data.info.amount_awaiting_finalization; + let amount_locked = data.info.amount_locked; // Show transactions info. View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { - let amount_awaiting_conf = data.info.amount_awaiting_confirmation; - let amount_awaiting_fin = data.info.amount_awaiting_finalization; - let amount_locked = data.info.amount_locked; // Show non-zero awaiting confirmation amount. if amount_awaiting_conf != 0 { @@ -117,7 +155,7 @@ impl WalletInfo { } // Show message when wallet txs are empty. - if txs_size == 0 { + if data.txs.is_empty() { View::center_content(ui, 96.0, |ui| { let empty_text = t!( "wallets.txs_empty", @@ -137,105 +175,139 @@ impl WalletInfo { .scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible) .id_source(Id::from("txs_content").with(wallet.get_config().id)) .auto_shrink([false; 2]) - .show_rows(ui, TX_ITEM_HEIGHT, txs_size, |ui, row_range| { + .show_rows(ui, TX_ITEM_HEIGHT, data.txs.len(), |ui, row_range| { ui.add_space(3.0); View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { - let amount_awaiting_conf = data.info.amount_awaiting_confirmation; - let amount_awaiting_fin = data.info.amount_awaiting_finalization; - let amount_locked = data.info.amount_locked; let extra_padding = amount_awaiting_conf != 0 || amount_awaiting_fin != 0 || amount_locked != 0; for index in row_range { - let tx = data.txs.get(index).unwrap(); - // Setup item rounding. - let item_rounding = View::item_rounding(index, txs_size, false); // Show transaction item. - tx_item_ui(ui, tx, item_rounding, extra_padding, &data, wallet); + let tx = data.txs.get(index).unwrap(); + let rounding = View::item_rounding(index, data.txs.len(), false); + self.tx_item_ui(ui, tx, rounding, extra_padding, true, &data, wallet, cb); } }); }); } -} -/// Height of transaction list item. -const TX_ITEM_HEIGHT: f32 = 76.0; - -/// Draw transaction item. -fn tx_item_ui(ui: &mut egui::Ui, - tx: &WalletTransaction, - mut rounding: Rounding, - extra_padding: bool, - data: &WalletData, - wallet: &mut Wallet) { - // Setup layout size. - let mut rect = ui.available_rect_before_wrap(); - if extra_padding { - rect.min += egui::emath::vec2(6.0, 0.0); - rect.max -= egui::emath::vec2(6.0, 0.0); + /// Draw [`Modal`] content for this ui container. + fn modal_content_ui(&mut self, + ui: &mut egui::Ui, + wallet: &mut Wallet, + cb: &dyn PlatformCallbacks) { + match Modal::opened() { + None => {} + Some(id) => { + match id { + TX_INFO_MODAL => { + Modal::ui(ui.ctx(), |ui, modal| { + self.tx_info_modal_ui(ui, wallet, modal, cb); + }); + } + _ => {} + } + } + } } - rect.set_height(TX_ITEM_HEIGHT); - // Draw round background. - let bg_rect = rect.clone(); - ui.painter().rect(bg_rect, rounding, Colors::BUTTON, View::ITEM_STROKE); + /// Draw transaction item. + fn tx_item_ui(&mut self, + ui: &mut egui::Ui, + tx: &WalletTransaction, + mut rounding: Rounding, + extra_padding: bool, + can_show_info: bool, + data: &WalletData, + wallet: &mut Wallet, + cb: &dyn PlatformCallbacks) { + // Setup layout size. + let mut rect = ui.available_rect_before_wrap(); + if extra_padding { + rect.min += egui::emath::vec2(6.0, 0.0); + rect.max -= egui::emath::vec2(6.0, 0.0); + } + rect.set_height(TX_ITEM_HEIGHT); + + // Draw round background. + let bg_rect = rect.clone(); + let color = if can_show_info { + Colors::BUTTON + } else { + Colors::FILL + }; + ui.painter().rect(bg_rect, rounding, color, View::ITEM_STROKE); - ui.vertical(|ui| { ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { - if extra_padding { - ui.add_space(-6.0); + // Draw button to show transaction info. + if can_show_info { + rounding.nw = 0.0; + rounding.sw = 0.0; + View::item_button(ui, rounding, FILE_TEXT, None, || { + self.tx_info_finalize = false; + self.show_tx_info_modal(wallet, tx); + }); } - // Draw button to show transaction info. - rounding.nw = 0.0; - rounding.sw = 0.0; - View::item_button(ui, rounding, FILE_TEXT, None, || { - //TODO: Show tx info - }); - - // 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) { - View::item_button(ui, Rounding::default(), PROHIBIT, Some(Colors::RED), || { + // Draw cancel button for tx that can be reposted and canceled. + if tx.can_repost(data) || tx.can_cancel() { + let cancel_rounding = if can_show_info { + Rounding::default() + } else { + rounding.nw = 0.0; + rounding.sw = 0.0; + rounding + }; + View::item_button(ui, cancel_rounding, PROHIBIT, Some(Colors::RED), || { wallet.cancel(tx.data.id); }); } - // Draw button to repost transaction. - if can_repost { - View::item_button(ui, - Rounding::default(), - ARROW_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()); - } + // Draw finalization button for tx that can be finalized. + if tx.can_finalize { + let (icon, color) = if !can_show_info && self.tx_info_finalize { + (FILE_TEXT, None) + } else { + (CHECK, Some(Colors::GREEN)) + }; + View::item_button(ui, Rounding::default(), icon, color, || { + if !can_show_info && self.tx_info_finalize { + self.tx_info_finalize = false; + return; + } + self.tx_info_finalize = true; + // Show transaction information modal. + if can_show_info { + self.show_tx_info_modal(wallet, tx); + cb.show_keyboard(); } }); } + // Draw button to repost transaction. + if tx.can_repost(data) { + View::item_button(ui, + Rounding::default(), + ARROW_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| { - if extra_padding { - ui.add_space(12.0); - } else { - ui.add_space(6.0); - } + ui.add_space(6.0); ui.vertical(|ui| { ui.add_space(3.0); @@ -294,13 +366,14 @@ fn tx_item_ui(ui: &mut egui::Ui, format!("{} {}", CHECK_CIRCLE, t!("wallets.tx_confirmed")) }, TxLogEntryType::TxSent | TxLogEntryType::TxReceived => { + let min_conf = data.info.minimum_confirmations; if data.info.last_confirmed_height - tx_height > min_conf { - let (icon, text) = if tx.data.tx_type == TxLogEntryType::TxSent { + let (i, t) = if tx.data.tx_type == TxLogEntryType::TxSent { (ARROW_CIRCLE_UP, t!("wallets.tx_sent")) } else { (ARROW_CIRCLE_DOWN, t!("wallets.tx_received")) }; - format!("{} {}", icon, text) + format!("{} {}", i, t) } else { let h = data.info.last_confirmed_height; let left_conf = h - tx_height; @@ -343,8 +416,269 @@ fn tx_item_ui(ui: &mut egui::Ui, let tx_time = View::format_time(tx.data.creation_ts.timestamp()); let tx_time_text = format!("{} {}", CALENDAR_CHECK, tx_time); ui.label(RichText::new(tx_time_text).size(15.0).color(Colors::GRAY)); + ui.add_space(3.0); }); }); }); - }); -} \ No newline at end of file + } + + /// Show transaction information [`Modal`]. + fn show_tx_info_modal(&mut self, wallet: &Wallet, tx: &WalletTransaction) { + self.tx_info_response_edit = "".to_string(); + self.tx_info_finalize_edit = "".to_string(); + self.tx_info_finalize_error = false; + self.tx_info_id = Some(tx.data.id); + // Setup slate and message from transaction. + if let Some((slate, message)) = wallet.read_slate_by_tx(tx) { + self.tx_info_response_edit = message; + self.tx_info_slate = Some(slate); + } + // Show transaction information modal. + Modal::new(TX_INFO_MODAL) + .position(ModalPosition::CenterTop) + .show(); + } + + /// Draw transaction info [`Modal`] content. + fn tx_info_modal_ui(&mut self, + ui: &mut egui::Ui, + wallet: &mut Wallet, + modal: &Modal, + cb: &dyn PlatformCallbacks) { + // Check values and setup transaction data. + let wallet_data = wallet.get_data(); + if wallet_data.is_none() { + modal.close(); + return; + } + let data = wallet_data.unwrap(); + let tx_id = self.tx_info_id.unwrap(); + let txs = data.txs.iter() + .filter(|tx| tx.data.id == tx_id) + .collect::>(); + if txs.is_empty() { + modal.close(); + return; + } + let tx = txs.get(0).unwrap(); + + ui.add_space(6.0); + + // Show transaction amount status and time. + let rounding = View::item_rounding(0, 2, false); + self.tx_item_ui(ui, tx, rounding, false, false, &data, wallet, cb); + + // Show transaction ID info. + if let Some(id) = tx.data.tx_slate_id { + let label = format!("{} {}", HASH_STRAIGHT, t!("id")); + Self::tx_info_modal_item_ui(ui, id.to_string(), label, true, cb); + } + // Show transaction kernel info. + if let Some(kernel) = tx.data.kernel_excess { + let label = format!("{} {}", FILE_ARCHIVE, t!("kernel")); + Self::tx_info_modal_item_ui(ui, kernel.0.to_hex(), label, true, cb); + } + + // Show transaction Slatepack message response or finalization input. + if !tx.posting && !tx.data.confirmed && (tx.data.tx_type == TxLogEntryType::TxSent || + tx.data.tx_type == TxLogEntryType::TxReceived) { + self.tx_info_modal_slate_ui(ui, tx, wallet, modal, cb); + } + ui.add_space(8.0); + + // Show button to close modal. + ui.vertical_centered_justified(|ui| { + View::button(ui, t!("close"), Colors::WHITE, || { + self.tx_info_id = None; + self.tx_info_finalize = false; + cb.hide_keyboard(); + modal.close(); + }); + }); + ui.add_space(6.0); + } + + /// Draw transaction information [`Modal`] item content. + fn tx_info_modal_item_ui(ui: &mut egui::Ui, + value: String, + label: String, + copy: bool, + cb: &dyn PlatformCallbacks) { + // Setup layout size. + let mut rect = ui.available_rect_before_wrap(); + rect.set_height(50.0); + + // Draw round background. + let bg_rect = rect.clone(); + let mut rounding = View::item_rounding(1, 3, false); + + ui.painter().rect(bg_rect, rounding, Colors::FILL, View::ITEM_STROKE); + + ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { + // Draw button to copy transaction info value. + if copy { + rounding.nw = 0.0; + rounding.sw = 0.0; + View::item_button(ui, rounding, COPY, None, || { + cb.copy_string_to_buffer(value.clone()); + }); + } + + // Draw value information. + let layout_size = ui.available_size(); + ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { + ui.add_space(6.0); + ui.vertical(|ui| { + ui.add_space(3.0); + View::ellipsize_text(ui, value, 15.0, Colors::TITLE); + ui.label(RichText::new(label).size(15.0).color(Colors::GRAY)); + ui.add_space(3.0); + }); + }); + }); + } + + /// Draw Slate content to show response or generate payment proof. + fn tx_info_modal_slate_ui(&mut self, + ui: &mut egui::Ui, + tx: &WalletTransaction, + wallet: &Wallet, + modal: &Modal, + cb: &dyn PlatformCallbacks) { + if self.tx_info_slate.is_none() { + return; + } + let slate = self.tx_info_slate.clone().unwrap(); + let amount = amount_to_hr_string(tx.amount, true); + + // Draw Slatepack message input or output description text. + ui.add_space(6.0); + ui.vertical_centered(|ui| { + if self.tx_info_finalize { + let desc_text = if self.tx_info_finalize_error { + t!("wallets.finalize_slatepack_err") + } else { + if tx.data.tx_type == TxLogEntryType::TxSent { + t!("wallets.parse_s2_slatepack_desc", "amount" => amount) + } else { + t!("wallets.parse_i2_slatepack_desc", "amount" => amount) + } + }; + let desc_color = if self.tx_info_finalize_error { + Colors::RED + } else { + Colors::INACTIVE_TEXT + }; + ui.label(RichText::new(desc_text).size(16.0).color(desc_color)); + } else { + let desc_text = if tx.can_finalize { + if tx.data.tx_type == TxLogEntryType::TxSent { + t!("wallets.send_request_desc", "amount" => amount) + } else { + t!("wallets.invoice_desc", "amount" => amount) + } + } else { + if tx.data.tx_type == TxLogEntryType::TxSent { + t!("wallets.parse_i1_slatepack_desc", "amount" => amount) + } else { + t!("wallets.parse_s1_slatepack_desc", "amount" => amount) + } + }; + ui.label(RichText::new(desc_text).size(16.0).color(Colors::INACTIVE_TEXT)); + } + }); + ui.add_space(4.0); + + ui.vertical_centered(|ui| { + let message_edit = if self.tx_info_finalize { + &mut self.tx_info_finalize_edit + } else { + &mut self.tx_info_response_edit + }; + let message_before = message_edit.clone(); + + // Draw Slatepack message text input or output. + let input_id = Id::from("tx_info_slatepack_message").with(slate.id).with(tx.data.id); + View::horizontal_line(ui, Colors::ITEM_STROKE); + ui.add_space(3.0); + ScrollArea::vertical() + .max_height(128.0) + .id_source(input_id) + .auto_shrink([false; 2]) + .show(ui, |ui| { + ui.add_space(7.0); + egui::TextEdit::multiline(message_edit) + .font(egui::TextStyle::Small) + .desired_rows(5) + .interactive(self.tx_info_finalize) + .hint_text(SLATEPACK_MESSAGE_HINT) + .desired_width(f32::INFINITY) + .show(ui); + ui.add_space(6.0); + }); + ui.add_space(2.0); + View::horizontal_line(ui, Colors::ITEM_STROKE); + ui.add_space(8.0); + + if self.tx_info_finalize { + // Draw paste button. + let paste_text = format!("{} {}", CLIPBOARD_TEXT, t!("paste")); + View::button(ui, paste_text, Colors::BUTTON, || { + self.tx_info_finalize_edit = cb.get_string_from_buffer(); + }); + + // Callback on finalization message input change. + if message_before != self.tx_info_finalize_edit { + self.on_finalization_input_change(tx, wallet, cb); + } + } else { + // Draw copy button. + let copy_text = format!("{} {}", COPY, t!("copy")); + View::button(ui, copy_text, Colors::BUTTON, || { + cb.copy_string_to_buffer(self.tx_info_response_edit.clone()); + self.tx_info_finalize_edit = "".to_string(); + if tx.can_finalize { + self.tx_info_finalize = true; + } else { + modal.close(); + } + }); + } + }); + } + + /// Parse Slatepack message on transaction finalization input change. + fn on_finalization_input_change(&mut self, + tx: &WalletTransaction, + wallet: &Wallet, + cb: &dyn PlatformCallbacks) { + let message = &self.tx_info_finalize_edit; + if message.is_empty() { + self.tx_info_finalize_error = false; + } else { + if let Ok(slate) = wallet.parse_slatepack(message) { + let send = slate.state == SlateState::Standard2 && + tx.data.tx_type == TxLogEntryType::TxSent; + let receive = slate.state == SlateState::Invoice2 && + tx.data.tx_type == TxLogEntryType::TxReceived; + if Some(slate.id) == tx.data.tx_slate_id && (send || receive) { + match wallet.finalize(message, wallet.can_use_dandelion()) { + Ok(_) => { + self.tx_info_finalize = false; + self.tx_info_finalize_edit = "".to_string(); + cb.hide_keyboard(); + } + Err(_) => { + self.tx_info_finalize_error = true; + } + } + } else { + self.tx_info_finalize_error = true; + } + } else { + self.tx_info_finalize_error = true; + } + } + } +} + diff --git a/src/wallet/types.rs b/src/wallet/types.rs index 6bc01bf..e7b1b66 100644 --- a/src/wallet/types.rs +++ b/src/wallet/types.rs @@ -17,7 +17,7 @@ use std::sync::Arc; use grin_keychain::ExtKeychain; use grin_util::Mutex; use grin_wallet_impls::{DefaultLCProvider, HTTPNodeClient}; -use grin_wallet_libwallet::{TxLogEntry, WalletInfo, WalletInst}; +use grin_wallet_libwallet::{TxLogEntry, TxLogEntryType, WalletInfo, WalletInst}; /// Mnemonic phrase setup mode. #[derive(PartialEq, Clone)] @@ -149,6 +149,25 @@ pub struct WalletTransaction { pub amount: u64, /// Flag to check if transaction is posting after finalization. pub posting: bool, + /// Flag to check if transaction can be finalized based on Slatepack message state. + pub can_finalize: bool, /// Last wallet block height of transaction reposting. pub repost_height: Option +} + +impl WalletTransaction { + /// Check if transaction can be cancelled. + pub fn can_cancel(&self) -> bool { + !self.posting && !self.data.confirmed && + self.data.tx_type != TxLogEntryType::TxReceivedCancelled + && self.data.tx_type != TxLogEntryType::TxSentCancelled + } + + /// Check if transaction can be reposted. + pub fn can_repost(&self, data: &WalletData) -> bool { + let last_height = data.info.last_confirmed_height; + let min_conf = data.info.minimum_confirmations; + self.posting && self.repost_height.is_some() && + last_height - self.repost_height.unwrap() > min_conf + } } \ No newline at end of file diff --git a/src/wallet/wallet.rs b/src/wallet/wallet.rs index e6ec17f..112df1c 100644 --- a/src/wallet/wallet.rs +++ b/src/wallet/wallet.rs @@ -479,9 +479,9 @@ impl Wallet { } /// Parse Slatepack message into [`Slate`]. - pub fn parse_slatepack(&self, message: String) -> Result { + pub fn parse_slatepack(&self, message: &String) -> Result { let api = Owner::new(self.instance.clone().unwrap(), None); - api.slate_from_slatepack_message(None, message, vec![]) + api.slate_from_slatepack_message(None, message.clone(), vec![]) } /// Create Slatepack message from provided slate. @@ -493,7 +493,7 @@ impl Wallet { Ok(()) })?; - // Save slatepack. + // Write Slatepack message to file. let slatepack_dir = self.get_config().get_slatepack_path(&slate); let mut output = File::create(slatepack_dir)?; output.write_all(message.as_bytes())?; @@ -510,7 +510,51 @@ impl Wallet { } } - /// Get transaction by slate id. + /// Get last stored [`Slate`] for transaction. + pub fn read_slate_by_tx(&self, tx: &WalletTransaction) -> Option<(Slate, String)> { + let mut slate = None; + if let Some(slate_id) = tx.data.tx_slate_id { + // Get slate state based on tx state and status. + let state = if tx.posting { + if tx.data.tx_type == TxLogEntryType::TxSent { + Some(SlateState::Standard3) + } else { + Some(SlateState::Invoice3) + } + } else if !tx.data.confirmed && (tx.data.tx_type == TxLogEntryType::TxSent || + tx.data.tx_type == TxLogEntryType::TxReceived) { + if tx.can_finalize { + if tx.data.tx_type == TxLogEntryType::TxSent { + Some(SlateState::Standard1) + } else { + Some(SlateState::Invoice1) + } + } else { + if tx.data.tx_type == TxLogEntryType::TxReceived { + Some(SlateState::Standard2) + } else { + Some(SlateState::Invoice2) + } + } + } else { + None + }; + // Get slate from state by reading Slatepack message file. + if let Some(st) = state { + let mut s = Slate::blank(0, false); + s.id = slate_id; + s.state = st; + if let Some(m) = self.read_slatepack(&s) { + if let Ok(s) = self.parse_slatepack(&m) { + slate = Some((s, m)); + } + } + } + } + slate + } + + /// Get transaction for [`Slate`] id. pub fn tx_by_slate(&self, slate: &Slate) -> Option { if let Some(data) = self.get_data() { let txs = data.txs.clone().iter().map(|tx| tx.clone()).filter(|tx| { @@ -571,7 +615,7 @@ impl Wallet { } /// Handle message from the invoice issuer to send founds, return response for funds receiver. - pub fn pay(&self, message: String) -> Result { + pub fn pay(&self, message: &String) -> Result { let slate = self.parse_slatepack(message)?; let config = self.get_config(); let args = InitTxArgs { @@ -595,7 +639,7 @@ impl Wallet { } /// Handle message to receive funds, return response to sender. - pub fn receive(&self, message: String) -> Result { + pub fn receive(&self, message: &String) -> Result { let mut slate = self.parse_slatepack(message)?; let api = Owner::new(self.instance.clone().unwrap(), None); controller::foreign_single_use(api.wallet_inst.clone(), None, |api| { @@ -612,7 +656,7 @@ impl Wallet { } /// Finalize transaction from provided message as sender or invoice issuer with Dandelion. - pub fn finalize(&self, message: String, dandelion: bool) -> Result { + pub fn finalize(&self, message: &String, dandelion: bool) -> Result { let mut slate = self.parse_slatepack(message)?; let api = Owner::new(self.instance.clone().unwrap(), None); slate = api.finalize_tx(None, &slate)?; @@ -629,7 +673,7 @@ 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. + // Setup transaction repost height, posting flag and ability to finalize. let mut slate = slate.clone(); if slate.state == SlateState::Invoice2 { slate.state = SlateState::Invoice3 @@ -643,6 +687,7 @@ impl Wallet { if t.data.id == tx.data.id { t.repost_height = Some(data.info.last_confirmed_height); t.posting = true; + t.can_finalize = false; } } *w_data = Some(data); @@ -656,23 +701,23 @@ impl Wallet { pub fn cancel(&mut self, id: u32) { let instance = self.instance.clone().unwrap(); let _ = cancel_tx(instance, None, &None, Some(id), None); - // Set cancelling status. - { - let mut w_data = self.data.write().unwrap(); - let mut data = w_data.clone().unwrap(); - let txs = data.txs.iter_mut().map(|tx| { - if tx.data.id == id { - tx.data.tx_type = if tx.data.tx_type == TxLogEntryType::TxReceived { - TxLogEntryType::TxReceivedCancelled - } else { - TxLogEntryType::TxSentCancelled - }; - } - tx.clone() - }).collect::>(); - data.txs = txs; - *w_data = Some(data); - } + // Setup cancelling status, posting flag, and ability to finalize. + let mut w_data = self.data.write().unwrap(); + let mut data = w_data.clone().unwrap(); + let txs = data.txs.iter_mut().map(|tx| { + if tx.data.id == id { + tx.posting = false; + tx.can_finalize = false; + tx.data.tx_type = if tx.data.tx_type == TxLogEntryType::TxReceived { + TxLogEntryType::TxReceivedCancelled + } else { + TxLogEntryType::TxSentCancelled + }; + } + tx.clone() + }).collect::>(); + data.txs = txs; + *w_data = Some(data); // Refresh wallet info to update statuses. self.sync(); } @@ -995,7 +1040,6 @@ fn sync_wallet_data(wallet: &Wallet) { // Create wallet txs. 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 @@ -1003,15 +1047,20 @@ fn sync_wallet_data(wallet: &Wallet) { tx.amount_credited - tx.amount_debited }; - // Setup transaction posting flag based on slate state. - let posting = if (tx.tx_type == TxLogEntryType::TxSent || - tx.tx_type == TxLogEntryType::TxReceived) && - !tx.confirmed && tx.tx_slate_id.is_some() { + let unconfirmed_sent_or_received = tx.tx_slate_id.is_some() && + !tx.confirmed && (tx.tx_type == TxLogEntryType::TxSent || + tx.tx_type == TxLogEntryType::TxReceived); + + // Setup transaction posting status based on slate state. + let posting = if unconfirmed_sent_or_received { + println!("{}", serde_json::to_string(tx).unwrap()); + // Create slate to check existing file. - let mut slate = Slate::blank(1, false); + let is_invoice = tx.tx_type == TxLogEntryType::TxReceived; + let mut slate = Slate::blank(0, is_invoice); slate.id = tx.tx_slate_id.unwrap(); - slate.state = match tx.tx_type { - TxLogEntryType::TxReceived => SlateState::Invoice3, + slate.state = match is_invoice { + true => SlateState::Invoice3, _ => SlateState::Standard3 }; @@ -1033,6 +1082,20 @@ fn sync_wallet_data(wallet: &Wallet) { false }; + // Setup flag for ability to finalize transaction. + let can_finalize = if !posting && unconfirmed_sent_or_received { + // Create slate to check existing file. + let mut slate = Slate::blank(1, false); + slate.id = tx.tx_slate_id.unwrap(); + slate.state = match tx.tx_type { + TxLogEntryType::TxReceived => SlateState::Invoice1, + _ => SlateState::Standard1 + }; + wallet.read_slatepack(&slate).is_some() + } else { + false + }; + // Setup reposting height. let mut repost_height = None; if posting { @@ -1046,10 +1109,12 @@ fn sync_wallet_data(wallet: &Wallet) { } } + // Add transaction to list. new_txs.push(WalletTransaction { data: tx.clone(), amount, posting, + can_finalize, repost_height, }) } -- 2.39.5 From 2c1be806a93432bb6da22150d365ee145bdf3bb0 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Wed, 24 Apr 2024 02:08:10 +0300 Subject: [PATCH 27/46] ui: tx info modal title --- locales/en.yml | 1 + locales/ru.yml | 1 + src/gui/views/wallets/wallet/txs.rs | 35 +++++++++++++++-------------- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/locales/en.yml b/locales/en.yml index 7ad1288..ffe2c67 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -67,6 +67,7 @@ wallets: tx_finalizing: Finalizing tx_confirmed: Confirmed txs: Transactions + tx: Transaction input_finalize_desc: 'Enter message to finalize the transaction:' messages: Messages transport: Transport diff --git a/locales/ru.yml b/locales/ru.yml index cc05e2a..e0a5142 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -67,6 +67,7 @@ wallets: tx_finalizing: Завершение tx_confirmed: Подтверждено txs: Транзакции + tx: Транзакция input_finalize_desc: 'Введите полученное сообщение для завершения транзакции:' messages: Сообщения transport: Транспорт diff --git a/src/gui/views/wallets/wallet/txs.rs b/src/gui/views/wallets/wallet/txs.rs index 2ff0dd5..14f5525 100644 --- a/src/gui/views/wallets/wallet/txs.rs +++ b/src/gui/views/wallets/wallet/txs.rs @@ -210,6 +210,24 @@ impl WalletTransactions { } } + /// Show transaction information [`Modal`]. + fn show_tx_info_modal(&mut self, wallet: &Wallet, tx: &WalletTransaction) { + self.tx_info_response_edit = "".to_string(); + self.tx_info_finalize_edit = "".to_string(); + self.tx_info_finalize_error = false; + self.tx_info_id = Some(tx.data.id); + // Setup slate and message from transaction. + if let Some((slate, message)) = wallet.read_slate_by_tx(tx) { + self.tx_info_response_edit = message; + self.tx_info_slate = Some(slate); + } + // Show transaction information modal. + Modal::new(TX_INFO_MODAL) + .position(ModalPosition::CenterTop) + .title(t!("wallets.tx")) + .show(); + } + /// Draw transaction item. fn tx_item_ui(&mut self, ui: &mut egui::Ui, @@ -422,23 +440,6 @@ impl WalletTransactions { }); } - /// Show transaction information [`Modal`]. - fn show_tx_info_modal(&mut self, wallet: &Wallet, tx: &WalletTransaction) { - self.tx_info_response_edit = "".to_string(); - self.tx_info_finalize_edit = "".to_string(); - self.tx_info_finalize_error = false; - self.tx_info_id = Some(tx.data.id); - // Setup slate and message from transaction. - if let Some((slate, message)) = wallet.read_slate_by_tx(tx) { - self.tx_info_response_edit = message; - self.tx_info_slate = Some(slate); - } - // Show transaction information modal. - Modal::new(TX_INFO_MODAL) - .position(ModalPosition::CenterTop) - .show(); - } - /// Draw transaction info [`Modal`] content. fn tx_info_modal_ui(&mut self, ui: &mut egui::Ui, -- 2.39.5 From 61a963ac2b2f6b28cef45d42bf5fe60e93518db5 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Wed, 24 Apr 2024 11:29:38 +0300 Subject: [PATCH 28/46] ui: settings modal, language selection --- Cargo.toml | 2 +- locales/en.yml | 3 + locales/ru.yml | 3 + src/config.rs | 24 +++++++- src/gui/views/wallets/content.rs | 98 +++++++++++++++++++++++++++++++- src/lib.rs | 30 ++++++---- 6 files changed, 145 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 73a0d0f..f8c8dd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ grin_wallet_controller = { git = "https://github.com/yeastplume/grin-wallet", br ## ui egui = { version = "0.27.2", default-features = false } egui_extras = { version = "0.27.2", features = ["image"] } -rust-i18n = "2.1.0" +rust-i18n = "2.3.1" ## other thiserror = "1.0.58" diff --git a/locales/en.yml b/locales/en.yml index ffe2c67..bf63107 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -1,3 +1,4 @@ +lang_name: English copy: Copy paste: Paste continue: Continue @@ -12,6 +13,8 @@ clear: Clear create: Create id: Identifier kernel: Kernel +settings: Settings +language: Language wallets: await_conf_amount: Awaiting confirmation await_fin_amount: Awaiting finalization diff --git a/locales/ru.yml b/locales/ru.yml index e0a5142..f2e2406 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -1,3 +1,4 @@ +lang_name: Русский copy: Копировать paste: Вставить continue: Продолжить @@ -12,6 +13,8 @@ clear: Очистить create: Создать id: Идентификатор kernel: Ядро +settings: Настройки +language: Язык wallets: await_conf_amount: Ожидает подтверждения await_fin_amount: Ожидает завершения diff --git a/src/config.rs b/src/config.rs index 93b72b0..d44a2f5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -37,9 +37,14 @@ pub struct AppConfig { height: f32, /// Position of the desktop window. - x: Option, y: Option + x: Option, y: Option, + + /// Locale code for i18n. + lang: Option } +pub const DEFAULT_LOCALE: &str = "en"; + pub const DEFAULT_WIDTH: f32 = 1200.0; pub const DEFAULT_HEIGHT: f32 = 720.0; @@ -54,6 +59,7 @@ impl Default for AppConfig { height: DEFAULT_HEIGHT, x: None, y: None, + lang: None, } } } @@ -170,4 +176,20 @@ impl AppConfig { } None } + + /// Save locale code. + pub fn save_locale(lang: &str) { + let mut w_app_config = Settings::app_config_to_update(); + w_app_config.lang = Some(lang.to_string()); + w_app_config.save(); + } + + /// Get current saved locale code. + pub fn locale() -> Option { + let r_config = Settings::app_config_to_read(); + if r_config.lang.is_some() { + return Some(r_config.lang.clone().unwrap()) + } + None + } } \ No newline at end of file diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index 8b602eb..f2324ab 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -17,9 +17,9 @@ use egui::{Align, Id, Layout, Margin, RichText, Rounding, ScrollArea, Widget}; use crate::AppConfig; use crate::gui::Colors; -use crate::gui::icons::{ARROW_LEFT, CARET_RIGHT, COMPUTER_TOWER, FOLDER_LOCK, FOLDER_OPEN, GEAR, GLOBE, GLOBE_SIMPLE, LOCK_KEY, PLUS, SIDEBAR_SIMPLE, SPINNER, SUITCASE, WARNING_CIRCLE}; +use crate::gui::icons::{ARROW_LEFT, CARET_RIGHT, CHECK, CHECK_FAT, COMPUTER_TOWER, FLAG, FOLDER_LOCK, FOLDER_OPEN, GEAR, GLOBE, GLOBE_SIMPLE, LOCK_KEY, PLUS, SIDEBAR_SIMPLE, SPINNER, SUITCASE, WARNING_CIRCLE}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, Root, TitlePanel, View}; +use crate::gui::views::{Modal, NodeSetup, Root, TitlePanel, View}; use crate::gui::views::types::{ModalContainer, ModalPosition, TextEditOptions, TitleContentType, TitleType}; use crate::gui::views::wallets::creation::WalletCreation; use crate::gui::views::wallets::types::WalletTabType; @@ -51,6 +51,9 @@ pub struct WalletsContent { /// Identifier for wallet opening [`Modal`]. const OPEN_WALLET_MODAL: &'static str = "open_wallet_modal"; +/// Identifier for wallet opening [`Modal`]. +const SETTINGS_MODAL: &'static str = "settings_modal"; + impl Default for WalletsContent { fn default() -> Self { Self { @@ -62,6 +65,7 @@ impl Default for WalletsContent { show_wallets_at_dual_panel: AppConfig::show_wallets_at_dual_panel(), modal_ids: vec![ OPEN_WALLET_MODAL, + SETTINGS_MODAL, WalletCreation::NAME_PASS_MODAL ] } @@ -83,6 +87,7 @@ impl ModalContainer for WalletsContent { WalletCreation::NAME_PASS_MODAL => { self.creation_content.name_pass_modal_ui(ui, modal, cb) }, + SETTINGS_MODAL => self.settings_modal_ui(ui, modal), _ => {} } } @@ -298,7 +303,11 @@ impl WalletsContent { }; }, |ui, frame| { View::title_button(ui, GEAR, || { - //TODO: show settings. + // Show settings modal. + Modal::new(SETTINGS_MODAL) + .position(ModalPosition::CenterTop) + .title(t!("settings")) + .show(); }); }, ui, frame); } @@ -586,6 +595,89 @@ impl WalletsContent { }); } + /// Draw creating wallet name/password input [`Modal`] content. + pub fn settings_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal) { + ui.add_space(6.0); + + // Draw chain type selection. + NodeSetup::chain_type_ui(ui); + + ui.add_space(8.0); + View::horizontal_line(ui, Colors::ITEM_STROKE); + ui.add_space(6.0); + + ui.vertical_centered(|ui| { + ui.label(RichText::new(format!("{} {}", GLOBE_SIMPLE, t!("language"))) + .size(16.0) + .color(Colors::GRAY) + ); + }); + ui.add_space(6.0); + + // Draw available list of languages to select. + let locales = rust_i18n::available_locales!(); + for (index, locale) in locales.iter().enumerate() { + Self::language_item_ui(locale, ui, index, locales.len(), modal); + } + + ui.add_space(8.0); + + // Show button to close modal. + ui.vertical_centered_justified(|ui| { + View::button(ui, t!("close"), Colors::WHITE, || { + modal.close(); + }); + }); + ui.add_space(6.0); + } + + /// Draw language selection item content. + fn language_item_ui(locale: &str, ui: &mut egui::Ui, index: usize, len: usize, modal: &Modal) { + // Setup layout size. + let mut rect = ui.available_rect_before_wrap(); + rect.set_height(50.0); + + // Draw round background. + let bg_rect = rect.clone(); + let item_rounding = View::item_rounding(index, len, false); + ui.painter().rect(bg_rect, item_rounding, Colors::FILL, View::ITEM_STROKE); + + ui.vertical(|ui| { + ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { + // Draw button to select language. + let is_current = if let Some(lang) = AppConfig::locale() { + lang == locale + } else { + rust_i18n::locale() == locale + }; + if !is_current { + View::item_button(ui, View::item_rounding(index, len, true), CHECK, None, || { + rust_i18n::set_locale(locale); + AppConfig::save_locale(locale); + modal.close(); + }); + } else { + ui.add_space(14.0); + ui.label(RichText::new(CHECK_FAT).size(20.0).color(Colors::GREEN)); + ui.add_space(14.0); + } + + 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); + ui.vertical(|ui| { + // Draw language name. + ui.add_space(12.0); + ui.label(RichText::new(t!("lang_name", locale = locale)) + .size(17.0) + .color(Colors::TEXT)); + ui.add_space(3.0); + }); + }); + }); + }); + } + /// Handle Back key event. /// Return `false` when event was handled. pub fn on_back(&mut self) -> bool { diff --git a/src/lib.rs b/src/lib.rs index dad94d5..1340d40 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,14 +15,14 @@ #[macro_use] extern crate rust_i18n; -use eframe::wgpu; -use egui::{Context, Stroke, vec2}; +use egui::{Context, Stroke}; #[cfg(target_os = "android")] use winit::platform::android::activity::AndroidApp; pub use config::AppConfig; pub use settings::Settings; +use crate::config::DEFAULT_LOCALE; use crate::gui::{Colors, PlatformApp}; use crate::gui::platform::PlatformCallbacks; @@ -206,14 +206,24 @@ pub fn setup_fonts(ctx: &Context) { /// Setup translations. fn setup_i18n() { - const DEFAULT_LOCALE: &str = "en"; - let locale = sys_locale::get_locale().unwrap_or(String::from(DEFAULT_LOCALE)); - let locale_str = if locale.contains("-") { - locale.split("-").next().unwrap_or(DEFAULT_LOCALE) + // Set saved locale or get from system. + if let Some(lang) = AppConfig::locale() { + if rust_i18n::available_locales!().contains(&lang.as_str()) { + rust_i18n::set_locale(lang.as_str()); + } } else { - locale.as_str() - }; - if _rust_i18n_available_locales().contains(&locale_str) { - rust_i18n::set_locale(locale_str); + let locale = sys_locale::get_locale().unwrap_or(String::from(DEFAULT_LOCALE)); + let locale_str = if locale.contains("-") { + locale.split("-").next().unwrap_or(DEFAULT_LOCALE) + } else { + locale.as_str() + }; + + // Set best possible locale. + if rust_i18n::available_locales!().contains(&locale_str) { + rust_i18n::set_locale(locale_str); + } else { + rust_i18n::set_locale(DEFAULT_LOCALE); + } } } \ No newline at end of file -- 2.39.5 From a93b040b836cea5d75f2a1c504c30d2f3fcbc849 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Wed, 24 Apr 2024 11:36:36 +0300 Subject: [PATCH 29/46] build: grin_wallet dependency from main repo --- Cargo.lock | 12 ++++++------ Cargo.toml | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 61d95ea..7c0e2c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2808,7 +2808,7 @@ dependencies = [ [[package]] name = "grin_wallet_api" version = "5.2.0-beta.1" -source = "git+https://github.com/yeastplume/grin-wallet?branch=prevent_double_pay#6e6b16a61c53825447f27ad49ba654c922cf9702" +source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#297c8285321aaf7d1f7be6314fcf0fdc520753ee" dependencies = [ "base64 0.12.3", "chrono", @@ -2833,7 +2833,7 @@ dependencies = [ [[package]] name = "grin_wallet_config" version = "5.2.0-beta.1" -source = "git+https://github.com/yeastplume/grin-wallet?branch=prevent_double_pay#6e6b16a61c53825447f27ad49ba654c922cf9702" +source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#297c8285321aaf7d1f7be6314fcf0fdc520753ee" dependencies = [ "dirs 2.0.2", "grin_core", @@ -2848,7 +2848,7 @@ dependencies = [ [[package]] name = "grin_wallet_controller" version = "5.2.0-beta.1" -source = "git+https://github.com/yeastplume/grin-wallet?branch=prevent_double_pay#6e6b16a61c53825447f27ad49ba654c922cf9702" +source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#297c8285321aaf7d1f7be6314fcf0fdc520753ee" dependencies = [ "chrono", "easy-jsonrpc-mw", @@ -2882,7 +2882,7 @@ dependencies = [ [[package]] name = "grin_wallet_impls" version = "5.2.0-beta.1" -source = "git+https://github.com/yeastplume/grin-wallet?branch=prevent_double_pay#6e6b16a61c53825447f27ad49ba654c922cf9702" +source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#297c8285321aaf7d1f7be6314fcf0fdc520753ee" dependencies = [ "base64 0.12.3", "blake2-rfc", @@ -2921,7 +2921,7 @@ dependencies = [ [[package]] name = "grin_wallet_libwallet" version = "5.2.0-beta.1" -source = "git+https://github.com/yeastplume/grin-wallet?branch=prevent_double_pay#6e6b16a61c53825447f27ad49ba654c922cf9702" +source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#297c8285321aaf7d1f7be6314fcf0fdc520753ee" dependencies = [ "age", "base64 0.9.3", @@ -2958,7 +2958,7 @@ dependencies = [ [[package]] name = "grin_wallet_util" version = "5.2.0-beta.1" -source = "git+https://github.com/yeastplume/grin-wallet?branch=prevent_double_pay#6e6b16a61c53825447f27ad49ba654c922cf9702" +source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#297c8285321aaf7d1f7be6314fcf0fdc520753ee" dependencies = [ "data-encoding", "ed25519-dalek", diff --git a/Cargo.toml b/Cargo.toml index f8c8dd3..d7f97c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,11 +26,11 @@ grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "master grin_util = { git = "https://github.com/mimblewimble/grin", branch = "master" } ## wallet -grin_wallet_impls = { git = "https://github.com/yeastplume/grin-wallet", branch = "prevent_double_pay" } -grin_wallet_api = { git = "https://github.com/yeastplume/grin-wallet", branch = "prevent_double_pay" } -grin_wallet_libwallet = { git = "https://github.com/yeastplume/grin-wallet", branch = "prevent_double_pay" } -grin_wallet_util = { git = "https://github.com/yeastplume/grin-wallet", branch = "prevent_double_pay" } -grin_wallet_controller = { git = "https://github.com/yeastplume/grin-wallet", branch = "prevent_double_pay" } +grin_wallet_impls = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } +grin_wallet_api = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } +grin_wallet_libwallet = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } +grin_wallet_util = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } +grin_wallet_controller = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } ## ui egui = { version = "0.27.2", default-features = false } -- 2.39.5 From 5a77bc07593c12e2036bb1a818ba1cf039aeb0eb Mon Sep 17 00:00:00 2001 From: ardocrat Date: Wed, 24 Apr 2024 18:24:21 +0300 Subject: [PATCH 30/46] settings: move to separate module --- src/lib.rs | 10 ++++------ src/{ => settings}/config.rs | 9 +++++---- src/settings/mod.rs | 19 +++++++++++++++++++ src/{ => settings}/settings.rs | 2 +- src/wallet/connections/config.rs | 3 +-- 5 files changed, 30 insertions(+), 13 deletions(-) rename src/{ => settings}/config.rs (97%) create mode 100644 src/settings/mod.rs rename src/{ => settings}/settings.rs (99%) diff --git a/src/lib.rs b/src/lib.rs index 1340d40..b0519f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,9 +20,8 @@ use egui::{Context, Stroke}; #[cfg(target_os = "android")] use winit::platform::android::activity::AndroidApp; -pub use config::AppConfig; +pub use settings::AppConfig; pub use settings::Settings; -use crate::config::DEFAULT_LOCALE; use crate::gui::{Colors, PlatformApp}; use crate::gui::platform::PlatformCallbacks; @@ -35,7 +34,6 @@ mod node; mod wallet; mod settings; -mod config; pub mod gui; // Include build information. @@ -212,9 +210,9 @@ fn setup_i18n() { rust_i18n::set_locale(lang.as_str()); } } else { - let locale = sys_locale::get_locale().unwrap_or(String::from(DEFAULT_LOCALE)); + let locale = sys_locale::get_locale().unwrap_or(String::from(AppConfig::DEFAULT_LOCALE)); let locale_str = if locale.contains("-") { - locale.split("-").next().unwrap_or(DEFAULT_LOCALE) + locale.split("-").next().unwrap_or(AppConfig::DEFAULT_LOCALE) } else { locale.as_str() }; @@ -223,7 +221,7 @@ fn setup_i18n() { if rust_i18n::available_locales!().contains(&locale_str) { rust_i18n::set_locale(locale_str); } else { - rust_i18n::set_locale(DEFAULT_LOCALE); + rust_i18n::set_locale(AppConfig::DEFAULT_LOCALE); } } } \ No newline at end of file diff --git a/src/config.rs b/src/settings/config.rs similarity index 97% rename from src/config.rs rename to src/settings/config.rs index d44a2f5..f847919 100644 --- a/src/config.rs +++ b/src/settings/config.rs @@ -43,10 +43,8 @@ pub struct AppConfig { lang: Option } -pub const DEFAULT_LOCALE: &str = "en"; - -pub const DEFAULT_WIDTH: f32 = 1200.0; -pub const DEFAULT_HEIGHT: f32 = 720.0; +const DEFAULT_WIDTH: f32 = 1200.0; +const DEFAULT_HEIGHT: f32 = 720.0; impl Default for AppConfig { fn default() -> Self { @@ -68,6 +66,9 @@ impl AppConfig { /// Application configuration file name. pub const FILE_NAME: &'static str = "app.toml"; + /// Default i18n locale. + pub const DEFAULT_LOCALE: &'static str = "en"; + /// Save application configuration to the file. pub fn save(&self) { Settings::write_to_file(self, Settings::get_config_path(Self::FILE_NAME, None)); diff --git a/src/settings/mod.rs b/src/settings/mod.rs new file mode 100644 index 0000000..1e93697 --- /dev/null +++ b/src/settings/mod.rs @@ -0,0 +1,19 @@ +// Copyright 2024 The Grim Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod settings; +pub use settings::Settings; + +mod config; +pub use config::AppConfig; \ No newline at end of file diff --git a/src/settings.rs b/src/settings/settings.rs similarity index 99% rename from src/settings.rs rename to src/settings/settings.rs index 1b32602..0f8de64 100644 --- a/src/settings.rs +++ b/src/settings/settings.rs @@ -22,8 +22,8 @@ use lazy_static::lazy_static; use serde::de::DeserializeOwned; use serde::Serialize; -use crate::config::AppConfig; use crate::node::NodeConfig; +use crate::settings::AppConfig; use crate::wallet::ConnectionsConfig; lazy_static! { diff --git a/src/wallet/connections/config.rs b/src/wallet/connections/config.rs index 37dd1a4..5ba9961 100644 --- a/src/wallet/connections/config.rs +++ b/src/wallet/connections/config.rs @@ -15,8 +15,7 @@ use grin_core::global::ChainTypes; use serde_derive::{Deserialize, Serialize}; -use crate::config::AppConfig; -use crate::Settings; +use crate::{AppConfig, Settings}; use crate::wallet::ExternalConnection; /// Wallet connections configuration. -- 2.39.5 From f095fa5ae0a2c0eeefeff805a2d3667c1c78706d Mon Sep 17 00:00:00 2001 From: ardocrat Date: Thu, 25 Apr 2024 12:02:09 +0300 Subject: [PATCH 31/46] android: cash dir env --- android/app/src/main/java/mw/gri/android/MainActivity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/android/app/src/main/java/mw/gri/android/MainActivity.java b/android/app/src/main/java/mw/gri/android/MainActivity.java index a595211..d280d70 100644 --- a/android/app/src/main/java/mw/gri/android/MainActivity.java +++ b/android/app/src/main/java/mw/gri/android/MainActivity.java @@ -39,6 +39,7 @@ public class MainActivity extends GameActivity { // Setup HOME environment variable for native code configurations. try { Os.setenv("HOME", getExternalFilesDir("").getPath(), true); + Os.setenv("XDG_CACHE_HOME", getExternalCacheDir().getPath(), true); } catch (ErrnoException e) { throw new RuntimeException(e); } -- 2.39.5 From 7207fb56bc930bb16e3fcb38a2d8761871b90f7a Mon Sep 17 00:00:00 2001 From: ardocrat Date: Thu, 25 Apr 2024 15:15:00 +0300 Subject: [PATCH 32/46] build + tor: update grin libs fix android build, add arti lib, tor server config, connection with snowflake, transport at connections --- Cargo.lock | 3236 +++++++++++++++-- Cargo.toml | 43 +- .../java/mw/gri/android/MainActivity.java | 1 + locales/en.yml | 6 + locales/ru.yml | 6 + src/gui/views/network/connections.rs | 80 +- src/gui/views/network/content.rs | 4 +- src/lib.rs | 5 +- src/settings/settings.rs | 20 + src/tor/config.rs | 56 + src/tor/mod.rs | 19 + src/tor/tor.rs | 228 ++ 12 files changed, 3436 insertions(+), 268 deletions(-) create mode 100644 src/tor/config.rs create mode 100644 src/tor/mod.rs create mode 100644 src/tor/tor.rs diff --git a/Cargo.lock b/Cargo.lock index 7c0e2c1..d429cb6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,6 +117,18 @@ dependencies = [ "generic-array 0.14.7", ] +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if 1.0.0", + "cipher 0.4.4", + "cpufeatures", + "zeroize", +] + [[package]] name = "age" version = "0.7.1" @@ -128,7 +140,7 @@ dependencies = [ "bech32 0.8.1", "chacha20poly1305", "cookie-factory", - "hkdf", + "hkdf 0.11.0", "hmac 0.11.0", "i18n-embed", "i18n-embed-fl", @@ -154,7 +166,7 @@ dependencies = [ "base64 0.13.1", "chacha20poly1305", "cookie-factory", - "hkdf", + "hkdf 0.11.0", "nom", "rand 0.8.5", "secrecy 0.8.0", @@ -196,6 +208,50 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +[[package]] +name = "amplify" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e711289a6cb28171b4f0e6c8019c69ff9476050508dc082167575d458ff74d0" +dependencies = [ + "amplify_derive", + "amplify_num", + "ascii", + "wasm-bindgen", +] + +[[package]] +name = "amplify_derive" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759dcbfaf94d838367a86d493ec34ccc8aa6fe365cb7880d6bf89006de24d9c1" +dependencies = [ + "amplify_syn", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 1.0.109", +] + +[[package]] +name = "amplify_num" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c009c5c4de814911b177e2ea59e4930bb918978ed3cce4900d846a6ceb0838" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "amplify_syn" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7736fb8d473c0d83098b5bac44df6a561e20470375cd8bcae30516dc889fd62a" +dependencies = [ + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 1.0.109", +] + [[package]] name = "android-activity" version = "0.5.2" @@ -256,6 +312,54 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "anyhow" version = "1.0.82" @@ -305,9 +409,9 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -341,12 +445,107 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "arti" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "277bd8bb409a6a9f2b9a7b903ac6125e66b024dd61a368d026086ddd73217899" +dependencies = [ + "anyhow", + "arti-client", + "backtrace", + "cfg-if 1.0.0", + "clap", + "derive_builder_fork_arti", + "educe", + "fs-mistrust", + "futures 0.3.30", + "hickory-proto", + "humantime 2.1.0", + "humantime-serde", + "itertools 0.12.1", + "libc", + "notify", + "paste", + "rlimit", + "safelog", + "secmem-proc", + "serde", + "thiserror", + "time", + "tokio 1.37.0", + "tokio-util 0.7.10", + "toml 0.8.12", + "tor-config", + "tor-error", + "tor-rtcompat", + "tor-socksproto", + "tracing", + "tracing-appender", + "tracing-subscriber", + "visibility", + "winapi 0.3.9", +] + +[[package]] +name = "arti-client" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4020944e05f8a92bf8772d76cc067495c7d040febd20286faa91fd33dc79ac7" +dependencies = [ + "anyhow", + "cfg-if 1.0.0", + "derive_builder_fork_arti", + "derive_more", + "directories", + "educe", + "fs-mistrust", + "futures 0.3.30", + "hostname-validator", + "humantime-serde", + "libc", + "pin-project", + "postage", + "safelog", + "serde", + "thiserror", + "tor-async-utils", + "tor-basic-utils", + "tor-cell", + "tor-chanmgr", + "tor-checkable", + "tor-circmgr", + "tor-config", + "tor-dirmgr", + "tor-error", + "tor-guardmgr", + "tor-hsclient", + "tor-hscrypto", + "tor-keymgr", + "tor-linkspec", + "tor-llcrypto", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-ptmgr", + "tor-rtcompat", + "tracing", + "void", +] + [[package]] name = "as-raw-xcb-connection" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + [[package]] name = "ash" version = "0.37.3+1.3.251" @@ -379,6 +578,22 @@ dependencies = [ "pin-project-lite 0.2.14", ] +[[package]] +name = "async-compression" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07dbbf24db18d609b1462965249abdf49129ccad073ec257da372adc83259c60" +dependencies = [ + "flate2", + "futures-core", + "futures-io", + "memchr", + "pin-project-lite 0.2.14", + "xz2", + "zstd", + "zstd-safe", +] + [[package]] name = "async-executor" version = "1.11.0" @@ -436,8 +651,8 @@ dependencies = [ "futures-io", "futures-lite 2.3.0", "parking", - "polling 3.6.0", - "rustix 0.38.32", + "polling 3.7.0", + "rustix 0.38.34", "slab", "tracing", "windows-sys 0.52.0", @@ -463,6 +678,18 @@ dependencies = [ "pin-project-lite 0.2.14", ] +[[package]] +name = "async-native-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9343dc5acf07e79ff82d0c37899f079db3534d99f189a1837c8e549c99405bec" +dependencies = [ + "futures-util", + "native-tls", + "thiserror", + "url", +] + [[package]] name = "async-once-cell" version = "0.5.3" @@ -482,7 +709,7 @@ dependencies = [ "cfg-if 1.0.0", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.32", + "rustix 0.38.34", "windows-sys 0.48.0", ] @@ -492,27 +719,27 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] name = "async-signal" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +checksum = "afe66191c335039c7bb78f99dc7520b0cbb166b3a1cb33a03f53d8a1c6f2afda" dependencies = [ "async-io 2.3.2", - "async-lock 2.8.0", + "async-lock 3.3.0", "atomic-waker", "cfg-if 1.0.0", "futures-core", "futures-io", - "rustix 0.38.32", + "rustix 0.38.34", "signal-hook-registry", "slab", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -527,11 +754,45 @@ version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] +[[package]] +name = "async_executors" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a982d2f86de6137cc05c9db9a915a19886c97911f9790d04f174cede74be01a5" +dependencies = [ + "blanket", + "futures-core", + "futures-task", + "futures-util", + "pin-project", + "rustc_version", + "tokio 1.37.0", +] + +[[package]] +name = "asynchronous-codec" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a860072022177f903e59730004fb5dc13db9275b79bb2aef7ba8ce831956c233" +dependencies = [ + "bytes 1.6.0", + "futures-sink", + "futures-util", + "memchr", + "pin-project-lite 0.2.14", +] + +[[package]] +name = "atomic" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59bdb34bc650a32731b31bd8f0829cc15d24a708ee31559e0bb34f2bc320cba" + [[package]] name = "atomic-waker" version = "1.1.2" @@ -639,6 +900,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.9.3" @@ -667,6 +934,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" + [[package]] name = "base64ct" version = "1.6.0" @@ -730,6 +1003,18 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06c9989a51171e2e81038ab168b6ae22886fe9ded214430dbb4f41c28cf176da" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake2-rfc" version = "0.2.18" @@ -740,6 +1025,17 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "blanket" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0b121a9fe0df916e362fb3271088d071159cdf11db0e4182d02152850756eff" +dependencies = [ + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", +] + [[package]] name = "block" version = "0.1.6" @@ -800,7 +1096,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7" dependencies = [ - "objc-sys 0.3.2", + "objc-sys 0.3.3", ] [[package]] @@ -839,6 +1135,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "bounded-vec-deque" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2225b558afc76c596898f5f1b3fc35cfce0eb1b13635cbd7d1b2a7177dc10ccd" + [[package]] name = "bs58" version = "0.3.1" @@ -870,6 +1172,12 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "by_address" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" + [[package]] name = "byte-tools" version = "0.3.1" @@ -891,9 +1199,9 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -902,6 +1210,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "0.4.12" @@ -932,8 +1246,8 @@ checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298" dependencies = [ "bitflags 2.5.0", "log", - "polling 3.6.0", - "rustix 0.38.32", + "polling 3.7.0", + "rustix 0.38.34", "slab", "thiserror", ] @@ -945,19 +1259,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02" dependencies = [ "calloop", - "rustix 0.38.32", + "rustix 0.38.34", "wayland-backend", "wayland-client", ] [[package]] -name = "cc" -version = "1.0.94" +name = "caret" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" +checksum = "2d55bb9904b8ef817fd66664cba4ecacac936781105c1016b0d2e13b900218db" + +[[package]] +name = "cc" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -1010,7 +1331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" dependencies = [ "cfg-if 1.0.0", - "cipher", + "cipher 0.3.0", "cpufeatures", "zeroize", ] @@ -1023,16 +1344,16 @@ checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" dependencies = [ "aead", "chacha20", - "cipher", + "cipher 0.3.0", "poly1305", "zeroize", ] [[package]] name = "chrono" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1053,10 +1374,49 @@ dependencies = [ ] [[package]] -name = "clipboard-win" -version = "5.3.0" +name = "cipher" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d517d4b86184dbb111d3556a10f1c8a04da7428d2987bf1081602bf11c3aa9ee" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + +[[package]] +name = "clap" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.11.1", + "terminal_size", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "clipboard-win" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79f4473f5144e20d9aceaf2972478f06ddf687831eafeeb434fbaf0acc4144ad" dependencies = [ "error-code", ] @@ -1070,6 +1430,17 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "coarsetime" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b3839cf01bb7960114be3ccf2340f541b6d0c81f8690b007b2b39f750f7e5d" +dependencies = [ + "libc", + "wasix", + "wasm-bindgen", +] + [[package]] name = "cocoa" version = "0.25.0" @@ -1116,6 +1487,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "com" version = "0.6.0" @@ -1132,7 +1509,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5" dependencies = [ "com_macros_support", - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "syn 1.0.109", ] @@ -1142,7 +1519,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", "syn 1.0.109", ] @@ -1166,12 +1543,37 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "config" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be" +dependencies = [ + "lazy_static", + "nom", + "pathdiff", + "serde", + "toml 0.8.12", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "constant_time_eq" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "cookie-factory" version = "0.3.3" @@ -1274,6 +1676,15 @@ dependencies = [ "cc", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -1293,6 +1704,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.19" @@ -1305,6 +1725,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -1355,6 +1787,15 @@ dependencies = [ "sct", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher 0.4.4", +] + [[package]] name = "cursor-icon" version = "1.1.0" @@ -1387,6 +1828,115 @@ dependencies = [ "zeroize", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", +] + +[[package]] +name = "d3d12" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e3d747f100290a1ca24b752186f61f6637e1deffe3bf6320de6fcb29510a307" +dependencies = [ + "bitflags 2.5.0", + "libloading 0.8.3", + "winapi 0.3.9", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core 0.14.4", + "darling_macro 0.14.4", +] + +[[package]] +name = "darling" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +dependencies = [ + "darling_core 0.20.8", + "darling_macro 0.20.8", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2 1.0.81", + "quote 1.0.36", + "strsim 0.10.0", + "syn 1.0.109", +] + +[[package]] +name = "darling_core" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2 1.0.81", + "quote 1.0.36", + "strsim 0.10.0", + "syn 2.0.60", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core 0.14.4", + "quote 1.0.36", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +dependencies = [ + "darling_core 0.20.8", + "quote 1.0.36", + "syn 2.0.60", +] + [[package]] name = "dashmap" version = "5.5.3" @@ -1406,17 +1956,136 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + [[package]] name = "derivative" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", "syn 1.0.109", ] +[[package]] +name = "derive-adhoc" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5283ac2881753c76c0892406705553f0d9ab30649f81e18964d3408f4501edb8" +dependencies = [ + "derive-adhoc-macros 0.7.3", + "heck 0.4.1", +] + +[[package]] +name = "derive-adhoc" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c768757217a40364c3af0b732d10bf9ed8af1e4f80b32fd5eeed94f375304b8" +dependencies = [ + "derive-adhoc-macros 0.8.4", + "heck 0.4.1", +] + +[[package]] +name = "derive-adhoc-macros" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c21b673a9b8c78c34908e6fcb42b922e11c4df2de5237f1c3f58d3285904a84b" +dependencies = [ + "heck 0.4.1", + "itertools 0.11.0", + "proc-macro-crate 1.3.1", + "proc-macro2 1.0.81", + "quote 1.0.36", + "sha3 0.10.8", + "strum 0.25.0", + "syn 1.0.109", + "void", +] + +[[package]] +name = "derive-adhoc-macros" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b41d3d974cda1ce7e9b40f7ec5160a099f20345bc3137447e526e3ba9d63245" +dependencies = [ + "heck 0.4.1", + "itertools 0.12.1", + "proc-macro-crate 2.0.0", + "proc-macro2 1.0.81", + "quote 1.0.36", + "sha3 0.10.8", + "strum 0.25.0", + "syn 2.0.60", + "void", +] + +[[package]] +name = "derive_builder_core_fork_arti" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24c1b715c79be6328caa9a5e1a387a196ea503740f0722ec3dd8f67a9e72314d" +dependencies = [ + "darling 0.14.4", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder_fork_arti" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3eae24d595f4d0ecc90a9a5a6d11c2bd8dafe2375ec4a1ec63250e5ade7d228" +dependencies = [ + "derive_builder_macro_fork_arti", +] + +[[package]] +name = "derive_builder_macro_fork_arti" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69887769a2489cd946bf782eb2b1bb2cb7bc88551440c94a765d4f040c08ebf3" +dependencies = [ + "derive_builder_core_fork_arti", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2 1.0.81", + "quote 1.0.36", + "rustc_version", + "syn 1.0.109", +] + [[package]] name = "digest" version = "0.8.1" @@ -1442,10 +2111,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", + "const-oid", "crypto-common", "subtle", ] +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys 0.4.1", +] + [[package]] name = "dirs" version = "2.0.2" @@ -1521,9 +2200,9 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -1550,6 +2229,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + [[package]] name = "easy-jsonrpc-mw" version = "0.5.4" @@ -1575,6 +2260,20 @@ dependencies = [ "syn 0.15.44", ] +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature 2.2.0", + "spki", +] + [[package]] name = "ecolor" version = "0.27.2" @@ -1591,7 +2290,17 @@ version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ - "signature", + "signature 1.6.4", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature 2.2.0", ] [[package]] @@ -1601,13 +2310,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" dependencies = [ "curve25519-dalek 3.2.0", - "ed25519", + "ed25519 1.5.3", "rand 0.7.3", "serde", "sha2 0.9.9", "zeroize", ] +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek 4.1.2", + "ed25519 2.2.3", + "merlin", + "rand_core 0.6.4", + "serde", + "sha2 0.10.8", + "subtle", + "zeroize", +] + +[[package]] +name = "educe" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" +dependencies = [ + "enum-ordinalize", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 1.0.109", +] + [[package]] name = "eframe" version = "0.27.2" @@ -1632,7 +2369,7 @@ dependencies = [ "percent-encoding", "pollster", "raw-window-handle 0.5.2", - "raw-window-handle 0.6.0", + "raw-window-handle 0.6.1", "static_assertions", "thiserror", "wasm-bindgen", @@ -1686,7 +2423,7 @@ dependencies = [ "arboard", "egui", "log", - "raw-window-handle 0.6.0", + "raw-window-handle 0.6.1", "smithay-clipboard", "web-time", "webbrowser", @@ -1729,6 +2466,25 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array 0.14.7", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "emath" version = "0.27.2" @@ -1754,6 +2510,18 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "enum-as-inner" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" +dependencies = [ + "heck 0.4.1", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", +] + [[package]] name = "enum-map" version = "2.7.3" @@ -1770,9 +2538,22 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", +] + +[[package]] +name = "enum-ordinalize" +version = "3.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" +dependencies = [ + "num-bigint 0.4.4", + "num-traits 0.2.18", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", ] [[package]] @@ -1800,9 +2581,9 @@ version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -1811,9 +2592,9 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -1949,6 +2730,18 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fastrand" version = "1.9.0" @@ -1973,6 +2766,34 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38793c55593b33412e3ae40c2c9781ffaa6f438f6f8c10f24e71846fbd7ae01e" + +[[package]] +name = "filetime" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.4.1", + "windows-sys 0.52.0", +] + [[package]] name = "find-crate" version = "0.6.3" @@ -2036,6 +2857,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "fluid-let" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "749cff877dc1af878a0b31a41dd221a753634401ea0ef2f87b62d3171522485a" + [[package]] name = "flume" version = "0.11.0" @@ -2076,9 +2903,9 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -2102,6 +2929,23 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs-mistrust" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5764b75624acb3ec878488145198a8fed761ca18fe26759d4ac4c7543fd2373a" +dependencies = [ + "derive_builder_fork_arti", + "dirs 5.0.1", + "educe", + "libc", + "once_cell", + "pwd-grp", + "serde", + "thiserror", + "walkdir", +] + [[package]] name = "fs2" version = "0.4.3" @@ -2112,6 +2956,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "fslock" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" +dependencies = [ + "libc", + "winapi 0.3.9", +] + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -2134,6 +2988,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.1.31" @@ -2222,9 +3082,9 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -2280,6 +3140,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -2310,8 +3171,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if 1.0.0", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -2360,6 +3223,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "glob-match" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985c9503b412198aa4197559e9a318524ebc4519c229bfa05a535828c950b9d" + [[package]] name = "globset" version = "0.4.14" @@ -2369,8 +3238,8 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", ] [[package]] @@ -2490,7 +3359,7 @@ dependencies = [ "presser", "thiserror", "winapi 0.3.9", - "windows 0.51.1", + "windows 0.52.0", ] [[package]] @@ -2519,7 +3388,10 @@ version = "0.1.0" dependencies = [ "android-activity", "android_logger", + "anyhow", "arboard", + "arti", + "arti-client", "built", "chrono", "dirs 5.0.1", @@ -2527,6 +3399,7 @@ dependencies = [ "egui", "egui_extras", "env_logger", + "fs-mistrust", "futures 0.3.30", "grin_api", "grin_chain", @@ -2557,14 +3430,18 @@ dependencies = [ "tokio 1.37.0", "tokio-util 0.7.10", "toml 0.8.12", + "tor-config", + "tor-rtcompat", "url", + "wgpu", "winit", ] [[package]] name = "grin_api" -version = "5.3.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#43b43d9749ea14a33e90da1e66a25197bcd91450" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9105a4b1db20827977575e56c8f6ea9f8f6bab2ac7f6abf95afbd29dafcd39" dependencies = [ "bytes 0.5.6", "easy-jsonrpc-mw", @@ -2575,7 +3452,7 @@ dependencies = [ "grin_pool", "grin_store", "grin_util", - "http", + "http 0.2.12", "hyper", "hyper-rustls 0.20.0", "hyper-timeout", @@ -2595,8 +3472,9 @@ dependencies = [ [[package]] name = "grin_chain" -version = "5.3.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#43b43d9749ea14a33e90da1e66a25197bcd91450" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2926f3e9444ec13781ddd6eeb4e4b2f8da68986c633b6d00b7c7cc4c2e87689" dependencies = [ "bit-vec", "bitflags 1.3.2", @@ -2618,8 +3496,9 @@ dependencies = [ [[package]] name = "grin_config" -version = "5.3.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#43b43d9749ea14a33e90da1e66a25197bcd91450" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e187a2686f4dc23395613505f7dc08c44fd141333a488868922b82cde70cb3d8" dependencies = [ "dirs 2.0.2", "grin_core", @@ -2634,8 +3513,9 @@ dependencies = [ [[package]] name = "grin_core" -version = "5.3.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#43b43d9749ea14a33e90da1e66a25197bcd91450" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "426d0e2f2fa27c26f931d4a21a236ff8ae16594151fdd0f8ce52fece85d94d89" dependencies = [ "blake2-rfc", "byteorder", @@ -2660,8 +3540,9 @@ dependencies = [ [[package]] name = "grin_keychain" -version = "5.3.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#43b43d9749ea14a33e90da1e66a25197bcd91450" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f93a5e9c18f2b443387051512e162fb130d4597e039a51fdc00a249578de9b5" dependencies = [ "blake2-rfc", "byteorder", @@ -2682,8 +3563,9 @@ dependencies = [ [[package]] name = "grin_p2p" -version = "5.3.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#43b43d9749ea14a33e90da1e66a25197bcd91450" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "415b7a30b128f127cd173572a9cf08c8bc072ef000ac778264b345f69e521fd8" dependencies = [ "bitflags 1.3.2", "bytes 0.5.6", @@ -2704,8 +3586,9 @@ dependencies = [ [[package]] name = "grin_pool" -version = "5.3.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#43b43d9749ea14a33e90da1e66a25197bcd91450" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa348a96f1e8721cd9109689ab2f54e8d278e32900da3d45faacee083237308" dependencies = [ "blake2-rfc", "chrono", @@ -2721,15 +3604,14 @@ dependencies = [ [[package]] name = "grin_secp256k1zkp" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b04798e32404c0af082b6a209ad4df1ab1d9ec3a5a5056f284cfa32f74e59ce6" +checksum = "447e2e66d2f6cc14d49afdad59a5b26f47654c845483dbb3f3403ace0dbb94b4" dependencies = [ "arrayvec 0.3.25", "cc", "libc", "rand 0.5.6", - "rustc-serialize", "serde", "serde_json", "zeroize", @@ -2737,8 +3619,9 @@ dependencies = [ [[package]] name = "grin_servers" -version = "5.3.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#43b43d9749ea14a33e90da1e66a25197bcd91450" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e360615506e0ba18dfb5ceee673106a24b878e138346db7488c1134cfd043bf5" dependencies = [ "chrono", "fs2", @@ -2751,7 +3634,7 @@ dependencies = [ "grin_pool", "grin_store", "grin_util", - "http", + "http 0.2.12", "hyper", "hyper-rustls 0.20.0", "lmdb-zero", @@ -2767,8 +3650,9 @@ dependencies = [ [[package]] name = "grin_store" -version = "5.3.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#43b43d9749ea14a33e90da1e66a25197bcd91450" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e32cd44bbbeaf7629f3fdaf166c6d3ef7d9198f9d3bbb06a3162cd5f3a8b407" dependencies = [ "byteorder", "croaring", @@ -2786,8 +3670,9 @@ dependencies = [ [[package]] name = "grin_util" -version = "5.3.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#43b43d9749ea14a33e90da1e66a25197bcd91450" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68e2e83d3ca5946b958e98ed4df1f1a7cf808ea74c0e4b4026e42e4a04d9ef92" dependencies = [ "backtrace", "base64 0.12.3", @@ -2807,13 +3692,14 @@ dependencies = [ [[package]] name = "grin_wallet_api" -version = "5.2.0-beta.1" -source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#297c8285321aaf7d1f7be6314fcf0fdc520753ee" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6efe4a404c3b67cba4c75c3a8af4c009bbdcad2fe013ec6a81f59ecf79970a" dependencies = [ "base64 0.12.3", "chrono", "easy-jsonrpc-mw", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "grin_core", "grin_keychain", "grin_util", @@ -2832,8 +3718,9 @@ dependencies = [ [[package]] name = "grin_wallet_config" -version = "5.2.0-beta.1" -source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#297c8285321aaf7d1f7be6314fcf0fdc520753ee" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcfbdf53cb183082d67b1fba346ee0490cb988d012647a853b7bba44e59fba95" dependencies = [ "dirs 2.0.2", "grin_core", @@ -2847,8 +3734,9 @@ dependencies = [ [[package]] name = "grin_wallet_controller" -version = "5.2.0-beta.1" -source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#297c8285321aaf7d1f7be6314fcf0fdc520753ee" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d716c6cf456dcb6ba5b091fe611d4f53dcd7559d0dc2805cc9d65650ff00cac1" dependencies = [ "chrono", "easy-jsonrpc-mw", @@ -2881,15 +3769,16 @@ dependencies = [ [[package]] name = "grin_wallet_impls" -version = "5.2.0-beta.1" -source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#297c8285321aaf7d1f7be6314fcf0fdc520753ee" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ddb40b2bc1263be4de92384868c97dd5bd670ba3209119912c143abda73547" dependencies = [ "base64 0.12.3", "blake2-rfc", "byteorder", "chrono", "data-encoding", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "futures 0.3.30", "grin_api", "grin_chain", @@ -2920,8 +3809,9 @@ dependencies = [ [[package]] name = "grin_wallet_libwallet" -version = "5.2.0-beta.1" -source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#297c8285321aaf7d1f7be6314fcf0fdc520753ee" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e3356a69caa77e9797714c2757a6a90995b996fde744da0b8e292fda8f5331f" dependencies = [ "age", "base64 0.9.3", @@ -2931,7 +3821,7 @@ dependencies = [ "byteorder", "chrono", "curve25519-dalek 2.1.3", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "grin_core", "grin_keychain", "grin_store", @@ -2948,8 +3838,8 @@ dependencies = [ "serde_derive", "serde_json", "sha2 0.8.2", - "strum", - "strum_macros", + "strum 0.18.0", + "strum_macros 0.18.0", "thiserror", "uuid", "x25519-dalek 0.6.0", @@ -2957,19 +3847,31 @@ dependencies = [ [[package]] name = "grin_wallet_util" -version = "5.2.0-beta.1" -source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#297c8285321aaf7d1f7be6314fcf0fdc520753ee" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af2dcb47e9ddbf8cad4f8a14fbf9514085e603007f52d26e4f2ad59671b2b609" dependencies = [ "data-encoding", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "grin_util", "rand 0.6.5", "serde", "serde_derive", - "sha3", + "sha3 0.8.2", "thiserror", ] +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "h2" version = "0.2.7" @@ -2981,7 +3883,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.12", "indexmap 1.9.3", "slab", "tokio 0.2.25", @@ -3016,6 +3918,15 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashlink" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692eaaf7f7607518dd3cef090f1474b61edc5301d8012f09579920df68b725ee" +dependencies = [ + "hashbrown 0.14.3", +] + [[package]] name = "hassle-rs" version = "0.11.0" @@ -3040,6 +3951,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "heck" version = "0.5.0" @@ -3064,6 +3981,30 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" +[[package]] +name = "hickory-proto" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" +dependencies = [ + "async-trait", + "cfg-if 1.0.0", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.4.0", + "ipnet", + "once_cell", + "rand 0.8.5", + "thiserror", + "tinyvec", + "tokio 1.37.0", + "tracing", + "url", +] + [[package]] name = "hkdf" version = "0.11.0" @@ -3074,6 +4015,15 @@ dependencies = [ "hmac 0.11.0", ] +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac 0.12.1", +] + [[package]] name = "hmac" version = "0.11.0" @@ -3102,6 +4052,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "hostname-validator" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f558a64ac9af88b5ba400d99b579451af0d39c6d360980045b91aac966d705e2" + [[package]] name = "http" version = "0.2.12" @@ -3113,6 +4069,17 @@ dependencies = [ "itoa 1.0.11", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes 1.6.0", + "fnv", + "itoa 1.0.11", +] + [[package]] name = "http-body" version = "0.3.1" @@ -3120,7 +4087,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" dependencies = [ "bytes 0.5.6", - "http", + "http 0.2.12", ] [[package]] @@ -3135,6 +4102,12 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "humantime" version = "1.3.0" @@ -3150,6 +4123,16 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime 2.1.0", + "serde", +] + [[package]] name = "hyper" version = "0.13.10" @@ -3161,10 +4144,10 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", + "http 0.2.12", "http-body", "httparse", - "httpdate", + "httpdate 0.3.2", "itoa 0.4.8", "pin-project", "socket2 0.3.19", @@ -3282,10 +4265,10 @@ dependencies = [ "i18n-embed", "lazy_static", "proc-macro-error", - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "strsim", - "syn 2.0.58", + "strsim 0.10.0", + "syn 2.0.60", "unic-langid", ] @@ -3297,9 +4280,9 @@ checksum = "81093c4701672f59416582fe3145676126fd23ba5db910acad0793c1108aaa58" dependencies = [ "find-crate", "i18n-config", - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -3313,7 +4296,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core 0.52.0", + "windows-core", ] [[package]] @@ -3336,6 +4319,22 @@ dependencies = [ "objc2 0.4.1", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "0.5.0" @@ -3356,7 +4355,7 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata", + "regex-automata 0.4.6", "same-file", "walkdir", "winapi-util", @@ -3401,11 +4400,11 @@ dependencies = [ [[package]] name = "image-webp" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a84a25dcae3ac487bc24ef280f9e20c79c9b1a3e5e32cbed3041d1c514aa87c" +checksum = "d730b085583c4d789dfd07fdcf185be59501666a90c97c40162b37e4fdad272d" dependencies = [ - "byteorder", + "byteorder-lite", "thiserror", ] @@ -3423,6 +4422,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg 1.2.0", "hashbrown 0.12.3", + "serde", ] [[package]] @@ -3433,6 +4433,36 @@ checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.3", + "serde", +] + +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array 0.14.7", ] [[package]] @@ -3450,9 +4480,9 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -3474,6 +4504,12 @@ dependencies = [ "unic-langid", ] +[[package]] +name = "inventory" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" + [[package]] name = "io-lifetimes" version = "1.0.11" @@ -3511,6 +4547,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.12.1" @@ -3556,9 +4601,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685a7d121ee3f65ae4fddd72b25a04bb36b6af81bc0828f7d5434c0fe60fa3a2" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" dependencies = [ "libc", ] @@ -3627,11 +4672,34 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] [[package]] name = "lebe" @@ -3698,6 +4766,12 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + [[package]] name = "libredox" version = "0.0.2" @@ -3719,6 +4793,17 @@ dependencies = [ "libc", ] +[[package]] +name = "libsqlite3-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "libz-sys" version = "1.1.16" @@ -3829,7 +4914,7 @@ dependencies = [ "log-mdc", "parking_lot 0.10.2", "serde", - "serde-value", + "serde-value 0.6.0", "serde_derive", "serde_json", "serde_yaml", @@ -3856,6 +4941,17 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "lzma-sys" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -3865,6 +4961,15 @@ dependencies = [ "libc", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "maybe-rayon" version = "0.1.1" @@ -3918,6 +5023,18 @@ dependencies = [ "autocfg 1.2.0", ] +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.6.4", + "zeroize", +] + [[package]] name = "metal" version = "0.27.0" @@ -4001,6 +5118,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", + "log", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.48.0", ] @@ -4099,7 +5217,7 @@ dependencies = [ "ndk-sys", "num_enum", "raw-window-handle 0.5.2", - "raw-window-handle 0.6.0", + "raw-window-handle 0.6.1", "thiserror", ] @@ -4137,7 +5255,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c168194d373b1e134786274020dae7fc5513d565ea2ebb9bc9ff17ffb69106d4" dependencies = [ "either", - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", "serde", "syn 1.0.109", @@ -4209,6 +5327,23 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "notify" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +dependencies = [ + "bitflags 2.5.0", + "filetime", + "inotify", + "kqueue", + "libc", + "log", + "mio 0.8.11", + "walkdir", + "windows-sys 0.48.0", +] + [[package]] name = "ntapi" version = "0.4.1" @@ -4218,6 +5353,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi 0.3.9", +] + [[package]] name = "num" version = "0.2.1" @@ -4254,6 +5399,23 @@ dependencies = [ "num-traits 0.2.18", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits 0.2.18", + "rand 0.8.5", + "smallvec", + "zeroize", +] + [[package]] name = "num-complex" version = "0.2.4" @@ -4264,15 +5426,21 @@ dependencies = [ "num-traits 0.2.18", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -4335,6 +5503,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg 1.2.0", + "libm", ] [[package]] @@ -4363,9 +5532,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ "proc-macro-crate 3.1.0", - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -4397,9 +5566,9 @@ checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" [[package]] name = "objc-sys" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c71324e4180d0899963fc83d9d241ac39e699609fc1025a850aadac8257459" +checksum = "da284c198fb9b7b0603f8635185e85fbd5b64ee154b1ed406d489077de2d6d60" [[package]] name = "objc2" @@ -4418,7 +5587,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" dependencies = [ - "objc-sys 0.3.2", + "objc-sys 0.3.3", "objc2-encode 3.0.0", ] @@ -4509,9 +5678,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -4566,6 +5735,15 @@ dependencies = [ "num-traits 0.2.18", ] +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits 0.2.18", +] + [[package]] name = "ordered-stream" version = "0.2.0" @@ -4576,6 +5754,12 @@ dependencies = [ "pin-project-lite 0.2.14", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "owned_ttf_parser" version = "0.20.0" @@ -4585,6 +5769,44 @@ dependencies = [ "ttf-parser", ] +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.8", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.8", +] + +[[package]] +name = "p521" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" +dependencies = [ + "base16ct", + "ecdsa", + "elliptic-curve", + "primeorder", + "rand_core 0.6.4", + "sha2 0.10.8", +] + [[package]] name = "parking" version = "2.2.0" @@ -4655,6 +5877,12 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "pbkdf2" version = "0.8.0" @@ -4677,12 +5905,63 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.5" @@ -4698,9 +5977,9 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -4732,12 +6011,39 @@ dependencies = [ "futures-io", ] +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "platforms" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" + [[package]] name = "png" version = "0.17.13" @@ -4769,15 +6075,15 @@ dependencies = [ [[package]] name = "polling" -version = "3.6.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c976a60b2d7e99d6f229e414670a9b85d13ac305cc6d1e9c134de58c5aaaf6" +checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3" dependencies = [ "cfg-if 1.0.0", "concurrent-queue", "hermit-abi", "pin-project-lite 0.2.14", - "rustix 0.38.32", + "rustix 0.38.34", "tracing", "windows-sys 0.52.0", ] @@ -4799,6 +6105,27 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "postage" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af3fb618632874fb76937c2361a7f22afd393c982a2165595407edc75b06d3c1" +dependencies = [ + "atomic", + "crossbeam-queue", + "futures 0.3.30", + "parking_lot 0.12.1", + "pin-project", + "static_assertions", + "thiserror", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -4825,6 +6152,25 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "priority-queue" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0bda9164fe05bc9225752d54aae413343c36f684380005398a6a8fde95fe785" +dependencies = [ + "autocfg 1.2.0", + "indexmap 1.9.3", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -4835,6 +6181,15 @@ dependencies = [ "toml_edit 0.19.15", ] +[[package]] +name = "proc-macro-crate" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" +dependencies = [ + "toml_edit 0.20.7", +] + [[package]] name = "proc-macro-crate" version = "3.1.0" @@ -4851,7 +6206,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", "syn 1.0.109", "version_check", @@ -4863,7 +6218,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", "version_check", ] @@ -4879,9 +6234,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] @@ -4902,7 +6257,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" dependencies = [ "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", +] + +[[package]] +name = "pwd-grp" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6955c41fd7e4283bdf6ff3e7218b7e3f8ef24c4236b31d22be050f4cfd5e2a2c" +dependencies = [ + "derive-adhoc 0.7.3", + "libc", + "paste", + "thiserror", ] [[package]] @@ -4956,9 +6323,15 @@ version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.5.6" @@ -5149,6 +6522,12 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "range-alloc" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" + [[package]] name = "rav1e" version = "0.7.1" @@ -5163,7 +6542,7 @@ dependencies = [ "built", "cfg-if 1.0.0", "interpolate_name", - "itertools", + "itertools 0.12.1", "libc", "libfuzzer-sys", "log", @@ -5207,9 +6586,9 @@ checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" [[package]] name = "raw-window-handle" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" +checksum = "8cc3bcbdb1ddfc11e700e62968e6b4cc9c75bb466464ad28fb61c5b2c964418b" [[package]] name = "rayon" @@ -5283,8 +6662,17 @@ checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -5295,9 +6683,15 @@ checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.3", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.3" @@ -5321,7 +6715,7 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "http", + "http 0.2.12", "http-body", "hyper", "hyper-rustls 0.21.0", @@ -5350,6 +6744,22 @@ dependencies = [ "winreg", ] +[[package]] +name = "retry-error" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7dea0c97723329951a3ed41ce0de8b8e389de3de4b815fb86ff57f6ab868f04" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle", +] + [[package]] name = "rgb" version = "0.8.37" @@ -5385,6 +6795,51 @@ dependencies = [ "opaque-debug 0.3.1", ] +[[package]] +name = "rlimit" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3560f70f30a0f16d11d01ed078a07740fe6b489667abc7c7b029155d9f21c3d8" +dependencies = [ + "libc", +] + +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest 0.10.7", + "num-bigint-dig", + "num-integer", + "num-traits 0.2.18", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "sha2 0.10.8", + "signature 2.2.0", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rusqlite" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae" +dependencies = [ + "bitflags 2.5.0", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", + "time", +] + [[package]] name = "rust-embed" version = "6.8.1" @@ -5402,10 +6857,10 @@ version = "6.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49b94b81e5b2c284684141a2fb9e2a31be90638caf040bf9afbc5a0416afe1ac" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", "rust-embed-utils", - "syn 2.0.58", + "syn 2.0.60", "walkdir", ] @@ -5440,13 +6895,13 @@ checksum = "8531ce329f57af4dc91cb56f22e17eeb6507f7e80d09c24073fc2c9d334624f1" dependencies = [ "glob", "once_cell", - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", "rust-i18n-support", "serde", "serde_json", "serde_yaml", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -5459,7 +6914,7 @@ dependencies = [ "lazy_static", "normpath", "once_cell", - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "regex", "serde", "serde_json", @@ -5480,10 +6935,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] -name = "rustc-serialize" -version = "0.3.25" +name = "rustc_version" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe834bc780604f4674073badbad26d7219cadfb4a2275802db12cbae17498401" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] [[package]] name = "rustix" @@ -5501,14 +6959,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.5.0", "errno", + "itoa 1.0.11", "libc", "linux-raw-sys 0.4.13", + "once_cell", "windows-sys 0.52.0", ] @@ -5562,6 +7022,19 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +[[package]] +name = "safelog" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ea892f8598a3682b6ed10ed9d8522b50366a792953eda37e1d9ddf01e183e3f" +dependencies = [ + "derive_more", + "educe", + "either", + "fluid-let", + "thiserror", +] + [[package]] name = "safemem" version = "0.3.3" @@ -5574,7 +7047,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c0fbb5f676da676c260ba276a8f43a8dc67cf02d1438423aeb1c677a7212686" dependencies = [ - "cipher", + "cipher 0.3.0", ] [[package]] @@ -5586,6 +7059,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "sanitize-filename" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ed72fbaf78e6f2d41744923916966c4fbe3d7c74e3037a8ee482f1115572603" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "schannel" version = "0.1.23" @@ -5642,6 +7125,34 @@ dependencies = [ "tiny-skia", ] +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array 0.14.7", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "secmem-proc" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e47e2571b899c6b81a5ff1e71ac7145947157af2125253fcf7033ab320ad7d32" +dependencies = [ + "anyhow", + "cfg-if 1.0.0", + "libc", + "rustix 0.38.34", + "thiserror", + "windows 0.52.0", +] + [[package]] name = "secrecy" version = "0.6.0" @@ -5722,10 +7233,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba" [[package]] -name = "serde" -version = "1.0.197" +name = "semver" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" + +[[package]] +name = "serde" +version = "1.0.198" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] @@ -5736,26 +7253,45 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a65a7291a8a568adcae4c10a677ebcedbc6c9cec91c054dee2ce40b0e3290eb" dependencies = [ - "ordered-float", + "ordered-float 1.1.1", + "serde", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float 2.10.1", "serde", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", +] + +[[package]] +name = "serde_ignored" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8e319a36d1b52126a0d608f24e93b2d81297091818cd70625fcf50a15d84ddf" +dependencies = [ + "serde", ] [[package]] name = "serde_json" -version = "1.0.115" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa 1.0.11", "ryu", @@ -5768,9 +7304,9 @@ version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -5794,6 +7330,36 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c85f8e96d1d6857f13768fcbd895fcb06225510022a2774ed8b5150581847b0" +dependencies = [ + "base64 0.22.0", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.2.6", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8b3a576c4eb2924262d5951a3b737ccaf16c931e39a2810c36f9a7e25575557" +dependencies = [ + "darling 0.20.8", + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", +] + [[package]] name = "serde_yaml" version = "0.8.26" @@ -5867,10 +7433,38 @@ dependencies = [ ] [[package]] -name = "signal-hook-registry" -version = "1.4.1" +name = "sha3" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shellexpand" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" +dependencies = [ + "dirs 5.0.1", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -5881,6 +7475,16 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -5896,6 +7500,18 @@ dependencies = [ "quote 1.0.36", ] +[[package]] +name = "simple_asn1" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +dependencies = [ + "num-bigint 0.4.4", + "num-traits 0.2.18", + "thiserror", + "time", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -5939,7 +7555,7 @@ dependencies = [ "libc", "log", "memmap2", - "rustix 0.38.32", + "rustix 0.38.34", "thiserror", "wayland-backend", "wayland-client", @@ -6026,6 +7642,57 @@ dependencies = [ "bitflags 2.5.0", ] +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "ssh-cipher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f" +dependencies = [ + "cipher 0.4.4", + "ssh-encoding", +] + +[[package]] +name = "ssh-encoding" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" +dependencies = [ + "base64ct", + "pem-rfc7468", + "sha2 0.10.8", +] + +[[package]] +name = "ssh-key" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca9b366a80cf18bb6406f4cf4d10aebfb46140a8c0c33f666a144c5c76ecbafc" +dependencies = [ + "p256", + "p384", + "p521", + "rand_core 0.6.4", + "rsa", + "sec1", + "sha2 0.10.8", + "signature 2.2.0", + "ssh-cipher", + "ssh-encoding", + "subtle", + "zeroize", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -6044,12 +7711,36 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros 0.25.3", +] + +[[package]] +name = "strum" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +dependencies = [ + "strum_macros 0.26.2", +] + [[package]] name = "strum_macros" version = "0.18.0" @@ -6057,11 +7748,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" dependencies = [ "heck 0.3.3", - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", "syn 1.0.109", ] +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck 0.4.1", + "proc-macro2 1.0.81", + "quote 1.0.36", + "rustversion", + "syn 2.0.60", +] + +[[package]] +name = "strum_macros" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +dependencies = [ + "heck 0.4.1", + "proc-macro2 1.0.81", + "quote 1.0.36", + "rustversion", + "syn 2.0.60", +] + [[package]] name = "subtle" version = "2.4.1" @@ -6091,18 +7808,18 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.58" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", "unicode-ident", ] @@ -6144,6 +7861,12 @@ dependencies = [ "version-compare", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "target-lexicon" version = "0.12.14" @@ -6158,7 +7881,7 @@ checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if 1.0.0", "fastrand 2.0.2", - "rustix 0.38.32", + "rustix 0.38.34", "windows-sys 0.52.0", ] @@ -6193,23 +7916,33 @@ dependencies = [ ] [[package]] -name = "thiserror" -version = "1.0.58" +name = "terminal_size" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix 0.38.34", + "windows-sys 0.48.0", +] + +[[package]] +name = "thiserror" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -6223,6 +7956,16 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", +] + [[package]] name = "tiff" version = "0.9.1" @@ -6234,6 +7977,37 @@ dependencies = [ "weezl", ] +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa 1.0.11", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "timer" version = "0.2.0" @@ -6351,7 +8125,7 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", "syn 1.0.109", ] @@ -6362,9 +8136,9 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -6450,6 +8224,7 @@ checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes 1.6.0", "futures-core", + "futures-io", "futures-sink", "pin-project-lite 0.2.14", "tokio 1.37.0", @@ -6486,7 +8261,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.9", + "toml_edit 0.22.12", ] [[package]] @@ -6511,6 +8286,17 @@ dependencies = [ "winnow 0.5.40", ] +[[package]] +name = "toml_edit" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap 2.2.6", + "toml_datetime", + "winnow 0.5.40", +] + [[package]] name = "toml_edit" version = "0.21.1" @@ -6524,9 +8310,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.9" +version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ "indexmap 2.2.6", "serde", @@ -6535,6 +8321,805 @@ dependencies = [ "winnow 0.6.6", ] +[[package]] +name = "tor-async-utils" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd81ab26694008630c7be8e14faf5bd1d78b177dd41be694d132d695a315556b" +dependencies = [ + "futures 0.3.30", + "pin-project", + "postage", + "thiserror", + "void", +] + +[[package]] +name = "tor-basic-utils" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2bfdcdf0279b4ba2ef744406cb4496403677c8854c306ccbf758eb378defd3" +dependencies = [ + "hex", + "libc", + "paste", + "rand 0.8.5", + "rand_chacha 0.3.1", + "slab", + "thiserror", +] + +[[package]] +name = "tor-bytes" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7538b9751dfe24c67977378dfd79ab21e4cc99641549fd12d9a8ba98a5752ee2" +dependencies = [ + "bytes 1.6.0", + "digest 0.10.7", + "educe", + "getrandom 0.2.14", + "thiserror", + "tor-error", + "tor-llcrypto", + "zeroize", +] + +[[package]] +name = "tor-cell" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b60d58a4f742830cf2e94b83f157e7c1ee3895bd55f912eede58c25835b5fb9" +dependencies = [ + "bitflags 2.5.0", + "bytes 1.6.0", + "caret", + "derive_more", + "educe", + "paste", + "rand 0.8.5", + "smallvec", + "thiserror", + "tor-basic-utils", + "tor-bytes", + "tor-cert", + "tor-error", + "tor-hscrypto", + "tor-linkspec", + "tor-llcrypto", + "tor-units", +] + +[[package]] +name = "tor-cert" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87bb9b74a5f5402768cee442147641d39ca2d0cba459f52fcca03cd8d978bd0d" +dependencies = [ + "caret", + "derive_more", + "digest 0.10.7", + "thiserror", + "tor-bytes", + "tor-checkable", + "tor-llcrypto", +] + +[[package]] +name = "tor-chanmgr" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30317ba968577ef37743d0b65c49816464de3f8642b81dea62206b7a86f4fc2" +dependencies = [ + "async-trait", + "derive_builder_fork_arti", + "derive_more", + "educe", + "futures 0.3.30", + "postage", + "rand 0.8.5", + "safelog", + "serde", + "thiserror", + "tor-async-utils", + "tor-basic-utils", + "tor-cell", + "tor-config", + "tor-error", + "tor-linkspec", + "tor-llcrypto", + "tor-netdir", + "tor-proto", + "tor-rtcompat", + "tor-socksproto", + "tor-units", + "tracing", + "void", +] + +[[package]] +name = "tor-checkable" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecb84b29d9a9500580cee42625a7ced01f7a7c45933c8ee81cd49ba10de2947" +dependencies = [ + "humantime 2.1.0", + "signature 2.2.0", + "thiserror", + "tor-llcrypto", +] + +[[package]] +name = "tor-circmgr" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "281cf6ef32c8800425aa219747c73a8c939a40038a4f4dbaefa9559892528093" +dependencies = [ + "amplify", + "async-trait", + "bounded-vec-deque", + "cfg-if 1.0.0", + "derive_builder_fork_arti", + "derive_more", + "downcast-rs", + "dyn-clone", + "educe", + "futures 0.3.30", + "humantime-serde", + "itertools 0.12.1", + "once_cell", + "pin-project", + "rand 0.8.5", + "retry-error", + "safelog", + "serde", + "static_assertions", + "thiserror", + "tor-async-utils", + "tor-basic-utils", + "tor-chanmgr", + "tor-config", + "tor-error", + "tor-guardmgr", + "tor-hscrypto", + "tor-linkspec", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-protover", + "tor-relay-selection", + "tor-rtcompat", + "tracing", + "void", + "weak-table", +] + +[[package]] +name = "tor-config" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df4e50e84389aeb81acf672f7dac9389a76d21505089db5cd3ae8e7d59374b2f" +dependencies = [ + "config", + "derive-adhoc 0.8.4", + "derive_builder_fork_arti", + "directories", + "educe", + "either", + "fs-mistrust", + "itertools 0.12.1", + "once_cell", + "paste", + "regex", + "serde", + "serde-value 0.7.0", + "serde_ignored", + "shellexpand", + "strum 0.26.2", + "thiserror", + "toml 0.8.12", + "tor-basic-utils", + "tor-error", + "tracing", + "void", +] + +[[package]] +name = "tor-consdiff" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91726266349ba3f10456c0bf0fb07fd0e6c3a344178d869eae93219ff78b54bb" +dependencies = [ + "digest 0.10.7", + "hex", + "thiserror", + "tor-llcrypto", +] + +[[package]] +name = "tor-dirclient" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9e102c3747d09963122edd07458c68c56e18034869102c63a6a37a0620c7e6f" +dependencies = [ + "async-compression", + "base64ct", + "derive_more", + "futures 0.3.30", + "hex", + "http 1.1.0", + "httparse", + "httpdate 1.0.3", + "itertools 0.12.1", + "memchr", + "thiserror", + "tor-circmgr", + "tor-error", + "tor-hscrypto", + "tor-linkspec", + "tor-llcrypto", + "tor-netdoc", + "tor-proto", + "tor-rtcompat", + "tracing", +] + +[[package]] +name = "tor-dirmgr" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90293b08ace33a5a49992eab54fa425c59d0fca7784865d9613ebaccdcaf630d" +dependencies = [ + "async-trait", + "base64ct", + "derive_builder_fork_arti", + "derive_more", + "digest 0.10.7", + "educe", + "event-listener 5.3.0", + "fs-mistrust", + "fslock", + "futures 0.3.30", + "hex", + "humantime 2.1.0", + "humantime-serde", + "itertools 0.12.1", + "memmap2", + "once_cell", + "paste", + "postage", + "rand 0.8.5", + "retry-error", + "rusqlite", + "safelog", + "scopeguard", + "serde", + "signature 2.2.0", + "strum 0.26.2", + "thiserror", + "time", + "tor-async-utils", + "tor-basic-utils", + "tor-checkable", + "tor-circmgr", + "tor-config", + "tor-consdiff", + "tor-dirclient", + "tor-error", + "tor-guardmgr", + "tor-llcrypto", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-rtcompat", + "tracing", +] + +[[package]] +name = "tor-error" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad73603da0675c5f16c8ee9f6e3349ad46c6645bd1653d59cc43fe2e5656234" +dependencies = [ + "backtrace", + "derive_more", + "futures 0.3.30", + "once_cell", + "paste", + "retry-error", + "static_assertions", + "strum 0.26.2", + "thiserror", + "tracing", +] + +[[package]] +name = "tor-guardmgr" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6821b8054aff8d0bf8d6f9004fc98487d5122237715704e448f03306ffbebf62" +dependencies = [ + "amplify", + "base64ct", + "derive_builder_fork_arti", + "derive_more", + "dyn-clone", + "educe", + "futures 0.3.30", + "humantime 2.1.0", + "humantime-serde", + "itertools 0.12.1", + "num_enum", + "pin-project", + "postage", + "rand 0.8.5", + "safelog", + "serde", + "strum 0.26.2", + "thiserror", + "tor-async-utils", + "tor-basic-utils", + "tor-config", + "tor-error", + "tor-linkspec", + "tor-llcrypto", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-protover", + "tor-relay-selection", + "tor-rtcompat", + "tor-units", + "tracing", +] + +[[package]] +name = "tor-hsclient" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833812aab4961e783329c7f0e0b0c218d4548a4225fce414405167aab29cf04e" +dependencies = [ + "async-trait", + "derive-adhoc 0.8.4", + "derive_more", + "educe", + "either", + "futures 0.3.30", + "itertools 0.12.1", + "postage", + "rand 0.8.5", + "rand_core 0.6.4", + "retry-error", + "safelog", + "slotmap", + "strum 0.26.2", + "thiserror", + "tor-async-utils", + "tor-basic-utils", + "tor-bytes", + "tor-cell", + "tor-checkable", + "tor-circmgr", + "tor-config", + "tor-dirclient", + "tor-error", + "tor-hscrypto", + "tor-keymgr", + "tor-linkspec", + "tor-llcrypto", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-rtcompat", + "tracing", +] + +[[package]] +name = "tor-hscrypto" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b0c899ce91d6fe6461f646d1e3c8d421dd5c8b570c0799540c4d4a2de80013" +dependencies = [ + "data-encoding", + "derive_more", + "digest 0.10.7", + "itertools 0.12.1", + "paste", + "rand 0.8.5", + "rand_core 0.6.4", + "safelog", + "serde", + "signature 2.2.0", + "subtle", + "thiserror", + "tor-basic-utils", + "tor-bytes", + "tor-error", + "tor-llcrypto", + "tor-units", +] + +[[package]] +name = "tor-keymgr" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907f80483a71d956f1654efbe994acea1082bf9517092af13a69f8d9f6423574" +dependencies = [ + "amplify", + "arrayvec 0.7.4", + "derive-adhoc 0.8.4", + "derive_builder_fork_arti", + "derive_more", + "downcast-rs", + "dyn-clone", + "fs-mistrust", + "glob-match", + "humantime 2.1.0", + "inventory", + "itertools 0.12.1", + "paste", + "rand 0.8.5", + "serde", + "ssh-key", + "thiserror", + "tor-basic-utils", + "tor-config", + "tor-error", + "tor-hscrypto", + "tor-llcrypto", + "tor-persist", + "walkdir", + "zeroize", +] + +[[package]] +name = "tor-linkspec" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7cb1a138572c49d4c7f3d5810801b35f3ade7450b9679161e8336c5deb04f2" +dependencies = [ + "base64ct", + "by_address", + "caret", + "cfg-if 1.0.0", + "derive_builder_fork_arti", + "derive_more", + "educe", + "hex", + "itertools 0.12.1", + "safelog", + "serde", + "serde_with", + "strum 0.26.2", + "thiserror", + "tor-basic-utils", + "tor-bytes", + "tor-config", + "tor-llcrypto", + "tor-protover", +] + +[[package]] +name = "tor-llcrypto" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca144066d80aabf43ff5d5ad2796de70d6eac26339fcfc4af38a0de8b27a1990" +dependencies = [ + "aes", + "base64ct", + "ctr", + "curve25519-dalek 4.1.2", + "derive_more", + "digest 0.10.7", + "ed25519-dalek 2.1.1", + "educe", + "getrandom 0.2.14", + "hex", + "rand_core 0.6.4", + "rsa", + "safelog", + "serde", + "sha1", + "sha2 0.10.8", + "sha3 0.10.8", + "signature 2.2.0", + "simple_asn1", + "subtle", + "thiserror", + "x25519-dalek 2.0.1", + "zeroize", +] + +[[package]] +name = "tor-log-ratelim" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab79604449b22d8dd9ff16a1a78a4ef98e606a1e5aa899c242528dffe42c1321" +dependencies = [ + "futures 0.3.30", + "humantime 2.1.0", + "once_cell", + "thiserror", + "tor-error", + "tor-rtcompat", + "tracing", + "weak-table", +] + +[[package]] +name = "tor-netdir" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be3dbedfe6e5d983ca3dc39ef699c0da7a3192f51da7ed466a2d7566389259b7" +dependencies = [ + "bitflags 2.5.0", + "derive_builder_fork_arti", + "derive_more", + "digest 0.10.7", + "futures 0.3.30", + "hex", + "humantime 2.1.0", + "itertools 0.12.1", + "num_enum", + "rand 0.8.5", + "serde", + "static_assertions", + "strum 0.26.2", + "thiserror", + "time", + "tor-basic-utils", + "tor-checkable", + "tor-config", + "tor-error", + "tor-hscrypto", + "tor-linkspec", + "tor-llcrypto", + "tor-netdoc", + "tor-protover", + "tor-units", + "tracing", + "typed-index-collections", +] + +[[package]] +name = "tor-netdoc" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20047c9f3104723d8966552ec4bd4c38057b5a0857705a6bbecedafb4cc0aef" +dependencies = [ + "amplify", + "base64ct", + "bitflags 2.5.0", + "cipher 0.4.4", + "derive_builder_fork_arti", + "derive_more", + "digest 0.10.7", + "educe", + "hex", + "humantime 2.1.0", + "itertools 0.12.1", + "once_cell", + "phf", + "rand 0.8.5", + "serde", + "serde_with", + "signature 2.2.0", + "smallvec", + "subtle", + "thiserror", + "time", + "tinystr", + "tor-basic-utils", + "tor-bytes", + "tor-cell", + "tor-cert", + "tor-checkable", + "tor-error", + "tor-hscrypto", + "tor-linkspec", + "tor-llcrypto", + "tor-protover", + "tor-units", + "weak-table", + "zeroize", +] + +[[package]] +name = "tor-persist" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7401ddb8c6a9ed71adeab421a20b786196409f02f389b415bb56041a5b0c80d4" +dependencies = [ + "derive-adhoc 0.8.4", + "derive_more", + "filetime", + "fs-mistrust", + "fslock", + "itertools 0.12.1", + "paste", + "sanitize-filename", + "serde", + "serde_json", + "thiserror", + "tor-basic-utils", + "tor-error", + "tracing", +] + +[[package]] +name = "tor-proto" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81e852aac27c0b8822ff6a1c5a32b24c7f05f1199e20c84caf34fdd4207139b2" +dependencies = [ + "asynchronous-codec", + "bitvec", + "bytes 1.6.0", + "cipher 0.4.4", + "coarsetime", + "derive_builder_fork_arti", + "derive_more", + "digest 0.10.7", + "educe", + "futures 0.3.30", + "hkdf 0.12.4", + "hmac 0.12.1", + "pin-project", + "rand 0.8.5", + "rand_core 0.6.4", + "safelog", + "subtle", + "thiserror", + "tokio 1.37.0", + "tokio-util 0.7.10", + "tor-async-utils", + "tor-basic-utils", + "tor-bytes", + "tor-cell", + "tor-cert", + "tor-checkable", + "tor-config", + "tor-error", + "tor-hscrypto", + "tor-linkspec", + "tor-llcrypto", + "tor-log-ratelim", + "tor-protover", + "tor-rtcompat", + "tor-rtmock", + "tor-units", + "tracing", + "typenum", + "visibility", + "void", + "zeroize", +] + +[[package]] +name = "tor-protover" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88c21ab6d4dd884741bf51373b7146cf9dee0be69172ef5bf20ff2dc0561bae9" +dependencies = [ + "caret", + "thiserror", +] + +[[package]] +name = "tor-ptmgr" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "886d3af3acf1a900d3ac0f7428a26741f8cd8fefbfdd7bd64f094445b3ae71da" +dependencies = [ + "async-trait", + "derive_builder_fork_arti", + "fs-mistrust", + "futures 0.3.30", + "itertools 0.12.1", + "serde", + "thiserror", + "tor-async-utils", + "tor-basic-utils", + "tor-chanmgr", + "tor-config", + "tor-error", + "tor-linkspec", + "tor-rtcompat", + "tor-socksproto", + "tracing", +] + +[[package]] +name = "tor-relay-selection" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd9d5ca40722685f05598b8f67c7033923493d0e38e83f04e1e69d3b21935dd" +dependencies = [ + "derive_more", + "rand 0.8.5", + "serde", + "tor-basic-utils", + "tor-linkspec", + "tor-netdir", + "tor-netdoc", +] + +[[package]] +name = "tor-rtcompat" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9974851ee6b7ba68a955cb67b56ac2164b4c7b520feb81533745f7ca119ed898" +dependencies = [ + "async-native-tls", + "async-trait", + "async_executors", + "coarsetime", + "derive_more", + "educe", + "futures 0.3.30", + "native-tls", + "paste", + "pin-project", + "thiserror", + "tokio 1.37.0", + "tokio-util 0.7.10", +] + +[[package]] +name = "tor-rtmock" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "576fe55ddf37e640ed2a020d81e3a4bc847551b6142640078b4a887415be2c6d" +dependencies = [ + "amplify", + "async-trait", + "backtrace", + "derive-adhoc 0.8.4", + "derive_more", + "educe", + "futures 0.3.30", + "humantime 2.1.0", + "itertools 0.12.1", + "pin-project", + "priority-queue", + "slotmap", + "strum 0.26.2", + "thiserror", + "tor-async-utils", + "tor-error", + "tor-rtcompat", + "tracing", + "tracing-test", + "void", +] + +[[package]] +name = "tor-socksproto" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3135cd3c23683a64cec756d960b72dae12ada350b0e825bfe5fcda811880ae76" +dependencies = [ + "caret", + "subtle", + "thiserror", + "tor-bytes", + "tor-error", +] + +[[package]] +name = "tor-units" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab81d120789c8639ceb374d23d180cbd4856e68458061400ecd9b6159bb6ca31" +dependencies = [ + "derive_more", + "thiserror", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -6553,15 +9138,27 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-appender" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +dependencies = [ + "crossbeam-channel", + "thiserror", + "time", + "tracing-subscriber", +] + [[package]] name = "tracing-attributes" version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -6571,6 +9168,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", ] [[package]] @@ -6583,6 +9181,58 @@ dependencies = [ "tracing", ] +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tracing-test" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a2c0ff408fe918a94c428a3f2ad04e4afd5c95bbc08fcf868eff750c15728a4" +dependencies = [ + "lazy_static", + "tracing-core", + "tracing-subscriber", + "tracing-test-macro", +] + +[[package]] +name = "tracing-test-macro" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258bc1c4f8e2e73a977812ab339d503e6feeb92700f6d07a6de4d321522d5c08" +dependencies = [ + "lazy_static", + "quote 1.0.36", + "syn 1.0.109", +] + [[package]] name = "traitobject" version = "0.1.0" @@ -6619,6 +9269,12 @@ dependencies = [ "rustc-hash", ] +[[package]] +name = "typed-index-collections" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183496e014253d15abbe6235677b1392dba2d40524c88938991226baa38ac7c4" + [[package]] name = "typemap" version = "0.3.3" @@ -6750,10 +9406,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna", + "idna 0.5.0", "percent-encoding", ] +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uuid" version = "0.8.2" @@ -6775,6 +9437,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" @@ -6793,6 +9461,23 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "visibility" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3fd98999db9227cf28e59d83e1f120f42bc233d4b152e8fab9bc87d5bb1e0f8" +dependencies = [ + "proc-macro2 1.0.81", + "quote 1.0.36", + "syn 2.0.60", +] + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "waker-fn" version = "1.1.1" @@ -6830,6 +9515,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasix" +version = "0.12.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1fbb4ef9bbca0c1170e0b00dd28abc9e3b68669821600cad1caaed606583c6d" +dependencies = [ + "wasi 0.11.0+wasi-snapshot-preview1", +] + [[package]] name = "wasm-bindgen" version = "0.2.92" @@ -6851,9 +9545,9 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", "wasm-bindgen-shared", ] @@ -6885,9 +9579,9 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6906,7 +9600,7 @@ checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40" dependencies = [ "cc", "downcast-rs", - "rustix 0.38.32", + "rustix 0.38.34", "scoped-tls", "smallvec", "wayland-sys", @@ -6919,7 +9613,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f" dependencies = [ "bitflags 2.5.0", - "rustix 0.38.32", + "rustix 0.38.34", "wayland-backend", "wayland-scanner", ] @@ -6941,7 +9635,7 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71ce5fa868dd13d11a0d04c5e2e65726d0897be8de247c0c5a65886e283231ba" dependencies = [ - "rustix 0.38.32", + "rustix 0.38.34", "wayland-client", "xcursor", ] @@ -6990,7 +9684,7 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quick-xml", "quote 1.0.36", ] @@ -7007,6 +9701,12 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "weak-table" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "323f4da9523e9a669e1eaf9c6e763892769b1d38c623913647bfdc1532fe4549" + [[package]] name = "web-sys" version = "0.3.69" @@ -7071,9 +9771,9 @@ checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "wgpu" -version = "0.19.3" +version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1213b52478a7631d6e387543ed8f642bc02c578ef4e3b49aca2a29a7df0cb" +checksum = "cbd7311dbd2abcfebaabf1841a2824ed7c8be443a0f29166e5d3c6a53a762c01" dependencies = [ "arrayvec 0.7.4", "cfg-if 1.0.0", @@ -7083,7 +9783,7 @@ dependencies = [ "naga", "parking_lot 0.12.1", "profiling", - "raw-window-handle 0.6.0", + "raw-window-handle 0.6.1", "smallvec", "static_assertions", "wasm-bindgen", @@ -7096,9 +9796,9 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "0.19.3" +version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9f6b033c2f00ae0bc8ea872c5989777c60bc241aac4e58b24774faa8b391f78" +checksum = "28b94525fc99ba9e5c9a9e24764f2bc29bad0911a7446c12f446a8277369bf3a" dependencies = [ "arrayvec 0.7.4", "bit-vec", @@ -7111,7 +9811,7 @@ dependencies = [ "once_cell", "parking_lot 0.12.1", "profiling", - "raw-window-handle 0.6.0", + "raw-window-handle 0.6.1", "rustc-hash", "smallvec", "thiserror", @@ -7122,17 +9822,19 @@ dependencies = [ [[package]] name = "wgpu-hal" -version = "0.19.3" +version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f972c280505ab52ffe17e94a7413d9d54b58af0114ab226b9fc4999a47082e" +checksum = "fc1a4924366df7ab41a5d8546d6534f1f33231aa5b3f72b9930e300f254e39c3" dependencies = [ "android_system_properties", "arrayvec 0.7.4", "ash", + "bit-set", "bitflags 2.5.0", "block", "cfg_aliases", "core-graphics-types", + "d3d12", "glow", "glutin_wgl_sys", "gpu-alloc", @@ -7151,7 +9853,8 @@ dependencies = [ "once_cell", "parking_lot 0.12.1", "profiling", - "raw-window-handle 0.6.0", + "range-alloc", + "raw-window-handle 0.6.1", "renderdoc-sys", "rustc-hash", "smallvec", @@ -7209,11 +9912,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "134306a13c5647ad6453e8deaec55d3a44d6021970129e6188735e74bf546697" dependencies = [ - "winapi 0.3.9", + "windows-sys 0.52.0", ] [[package]] @@ -7235,21 +9938,12 @@ dependencies = [ [[package]] name = "windows" -version = "0.51.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ - "windows-core 0.51.1", - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-core" -version = "0.51.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" -dependencies = [ - "windows-targets 0.48.5", + "windows-core", + "windows-targets 0.52.5", ] [[package]] @@ -7267,7 +9961,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e2ee588991b9e7e6c8338edf3333fbe4da35dc72092643958ebb43f0ab2c49c" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", "syn 1.0.109", ] @@ -7278,7 +9972,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6fb8df20c9bcaa8ad6ab513f7b40104840c8867d5751126e4df3b08388d0cc7" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", "syn 1.0.109", ] @@ -7516,9 +10210,9 @@ dependencies = [ "orbclient", "percent-encoding", "raw-window-handle 0.5.2", - "raw-window-handle 0.6.0", + "raw-window-handle 0.6.1", "redox_syscall 0.3.5", - "rustix 0.38.32", + "rustix 0.38.34", "sctk-adwaita", "smithay-client-toolkit", "smol_str", @@ -7574,6 +10268,15 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "x11-dl" version = "2.21.0" @@ -7596,7 +10299,7 @@ dependencies = [ "libc", "libloading 0.8.3", "once_cell", - "rustix 0.38.32", + "rustix 0.38.34", "x11rb-protocol", ] @@ -7628,6 +10331,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek 4.1.2", + "rand_core 0.6.4", + "serde", + "zeroize", +] + [[package]] name = "xcursor" version = "0.3.5" @@ -7669,6 +10384,15 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" +[[package]] +name = "xz2" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" +dependencies = [ + "lzma-sys", +] + [[package]] name = "yaml-rust" version = "0.4.5" @@ -7726,7 +10450,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", "regex", "syn 1.0.109", @@ -7759,16 +10483,16 @@ version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "63381fa6624bf92130a6b87c0d07380116f80b565c42cf0d754136f0238359ef" dependencies = [ "zeroize_derive", ] @@ -7779,9 +10503,9 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -7795,6 +10519,34 @@ dependencies = [ "thiserror", ] +[[package]] +name = "zstd" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.10+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "zune-core" version = "0.4.12" @@ -7840,7 +10592,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", "syn 1.0.109", "zvariant_utils", @@ -7852,7 +10604,7 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" dependencies = [ - "proc-macro2 1.0.79", + "proc-macro2 1.0.81", "quote 1.0.36", "syn 1.0.109", ] diff --git a/Cargo.toml b/Cargo.toml index d7f97c7..5f7ecaf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,21 +16,21 @@ log = "0.4" ## node openssl-sys = { version = "0.9.82", features = ["vendored"] } -grin_api = { git = "https://github.com/mimblewimble/grin", branch = "master" } -grin_chain = { git = "https://github.com/mimblewimble/grin", branch = "master" } -grin_config = { git = "https://github.com/mimblewimble/grin", branch = "master" } -grin_core = { git = "https://github.com/mimblewimble/grin", branch = "master" } -grin_p2p = { git = "https://github.com/mimblewimble/grin", branch = "master" } -grin_servers = { git = "https://github.com/mimblewimble/grin", branch = "master" } -grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "master" } -grin_util = { git = "https://github.com/mimblewimble/grin", branch = "master" } +grin_api = "5.3.0" +grin_chain = "5.3.0" +grin_config = "5.3.0" +grin_core = "5.3.0" +grin_p2p = "5.3.0" +grin_servers = "5.3.0" +grin_keychain = "5.3.0" +grin_util = "5.3.0" ## wallet -grin_wallet_impls = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } -grin_wallet_api = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } -grin_wallet_libwallet = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } -grin_wallet_util = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } -grin_wallet_controller = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } +grin_wallet_impls = "5.3.0" +grin_wallet_api = "5.3.0" +grin_wallet_libwallet = "5.3.0" +grin_wallet_util = "5.3.0" +grin_wallet_controller = "5.3.0" ## ui egui = { version = "0.27.2", default-features = false } @@ -38,6 +38,7 @@ egui_extras = { version = "0.27.2", features = ["image"] } rust-i18n = "2.3.1" ## other +anyhow = "1.0.75" thiserror = "1.0.58" futures = "0.3" dirs = "5.0.1" @@ -48,13 +49,20 @@ toml = "0.8.2" serde = "1.0.170" local-ip-address = "0.6.1" url = "2.4.0" - -## stratum server +rand = "0.8.5" serde_derive = "1.0.197" serde_json = "1.0.115" -tokio = {version = "1.29.1", features = ["full"] } +tokio = { version = "1.37.0", features = ["full"] } + +## tor +arti = { version = "1.2.0", features = ["experimental-api", "pt-client", "static"] } +arti-client = { version = "0.17.0", features = ["experimental-api", "pt-client", "static"] } +tor-rtcompat = { version = "0.17.0", features = ["static"] } +tor-config = "0.17.0" +fs-mistrust = "0.7.9" + +## stratum server tokio-util = { version = "0.7.8", features = ["codec"] } -rand = "0.8.5" [build-dependencies] built = { version = "0.7.0", features = ["git2"]} @@ -70,5 +78,6 @@ image = "0.25.1" android_logger = "0.13.1" jni = "0.21.1" android-activity = "0.5.2" +wgpu = "0.19" winit = { version = "0.29", features = [ "android-game-activity" ] } eframe = { version = "0.27.2", features = [ "wgpu", "android-game-activity" ] } \ No newline at end of file diff --git a/android/app/src/main/java/mw/gri/android/MainActivity.java b/android/app/src/main/java/mw/gri/android/MainActivity.java index d280d70..74bea5e 100644 --- a/android/app/src/main/java/mw/gri/android/MainActivity.java +++ b/android/app/src/main/java/mw/gri/android/MainActivity.java @@ -40,6 +40,7 @@ public class MainActivity extends GameActivity { try { Os.setenv("HOME", getExternalFilesDir("").getPath(), true); Os.setenv("XDG_CACHE_HOME", getExternalCacheDir().getPath(), true); + Os.setenv("ARTI_FS_DISABLE_PERMISSION_CHECKS", "true", true); } catch (ErrnoException e) { throw new RuntimeException(e); } diff --git a/locales/en.yml b/locales/en.yml index bf63107..64aa8f8 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -126,6 +126,12 @@ network: available: Available not_available: Not available availability_check: Availability check + tor_network: Tor network + server_enabled: Server enabled + server_starting: Server is starting + server_stopping: Server is stopping + server_error: Server error + server_disabled: Server disabled sync_status: node_restarting: Node is restarting node_down: Node is down diff --git a/locales/ru.yml b/locales/ru.yml index f2e2406..7e66a9f 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -126,6 +126,12 @@ network: available: Доступно not_available: Недоступно availability_check: Проверка доступности + tor_network: Сеть Tor + server_enabled: Сервер включен + server_starting: Сервер запускается + server_stopping: Сервер останавливается + server_error: Ошибка сервера + server_disabled: Сервер отключен sync_status: node_restarting: Узел перезапускается node_down: Узел выключен diff --git a/src/gui/views/network/connections.rs b/src/gui/views/network/connections.rs index 6819f37..853b99f 100644 --- a/src/gui/views/network/connections.rs +++ b/src/gui/views/network/connections.rs @@ -17,11 +17,12 @@ use url::Url; use crate::AppConfig; use crate::gui::Colors; -use crate::gui::icons::{CARET_RIGHT, CHECK_CIRCLE, COMPUTER_TOWER, DOTS_THREE_CIRCLE, PENCIL, POWER, TRASH, X_CIRCLE}; +use crate::gui::icons::{CARET_RIGHT, CHECK_CIRCLE, COMPUTER_TOWER, DOTS_THREE_CIRCLE, GEAR_SIX, PENCIL, POWER, TRASH, WARNING_CIRCLE, X_CIRCLE}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, NodeSetup, View}; use crate::gui::views::types::{ModalContainer, ModalPosition, TextEditOptions}; use crate::node::{Node, NodeConfig}; +use crate::tor::{TorServer, TorServerConfig}; use crate::wallet::{ConnectionsConfig, ExternalConnection}; /// Network connections content. @@ -99,12 +100,21 @@ impl ConnectionsContent { // Show integrated node info content. Self::integrated_node_item_ui(ui); + // Show transport connections. + ui.add_space(6.0); + let transport_text = format!("{}:", t!("wallets.transport")); + ui.label(RichText::new(transport_text).size(16.0).color(Colors::GRAY)); + ui.add_space(6.0); + + // Show Tor SOCKS server. + Self::tor_transport_item_ui(ui); + + // Show external connections. let ext_conn_list = ConnectionsConfig::ext_conn_list(); if !ext_conn_list.is_empty() { ui.add_space(6.0); ui.label(RichText::new(t!("wallets.ext_conn")).size(16.0).color(Colors::GRAY)); ui.add_space(6.0); - // Show external connections. for (index, conn) in ext_conn_list.iter().enumerate() { ui.horizontal_wrapped(|ui| { // Draw connection list item. @@ -114,6 +124,67 @@ impl ConnectionsContent { } } + /// Draw Tor connection item content. + fn tor_transport_item_ui(ui: &mut egui::Ui) { + // Draw round background. + let mut rect = ui.available_rect_before_wrap(); + rect.set_height(78.0); + let rounding = View::item_rounding(0, 1, false); + ui.painter().rect(rect, rounding, Colors::FILL, View::ITEM_STROKE); + + ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { + // Draw button to show Tor connection settings. + View::item_button(ui, View::item_rounding(0, 1, true), GEAR_SIX, None, || { + AppConfig::toggle_show_connections_network_panel(); + }); + + // Draw buttons to stop or start Tor server. + if !TorServer::is_stopping() && !TorServer::is_starting() { + if TorServer::is_running() { + View::item_button(ui, Rounding::default(), POWER, Some(Colors::RED), || { + TorServer::stop(); + }); + } else { + View::item_button(ui, Rounding::default(), POWER, Some(Colors::GREEN), || { + TorServer::start(); + }); + } + } + + let layout_size = ui.available_size(); + ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { + ui.add_space(6.0); + ui.vertical(|ui| { + ui.add_space(3.0); + ui.label(RichText::new(t!("network.tor_network")) + .size(18.0) + .color(Colors::TITLE)); + + // Setup SOCKS server address. + let socks_port = TorServerConfig::socks_port(); + let addr_text = format!("{} http://127.0.0.1:{}", COMPUTER_TOWER, socks_port); + ui.label(RichText::new(addr_text).size(15.0).color(Colors::TEXT)); + ui.add_space(1.0); + + // Setup server status text. + let (status_icon, status_text) = if TorServer::has_error() { + (WARNING_CIRCLE, t!("network.server_error")) + } else if TorServer::is_starting() { + (DOTS_THREE_CIRCLE, t!("network.server_starting")) + } else if TorServer::is_stopping() { + (DOTS_THREE_CIRCLE, t!("network.server_stopping")) + } else if TorServer::is_running() { + (CHECK_CIRCLE, t!("network.server_enabled")) + } else { + (X_CIRCLE, t!("network.server_disabled")) + }; + let status_text = format!("{} {}", status_icon, status_text); + ui.label(RichText::new(status_text).size(15.0).color(Colors::GRAY)); + }) + }); + }); + } + /// Draw integrated node connection item content. fn integrated_node_item_ui(ui: &mut egui::Ui) { // Draw round background. @@ -123,9 +194,6 @@ impl ConnectionsContent { ui.painter().rect(rect, rounding, Colors::FILL, View::ITEM_STROKE); ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { - // Setup padding for item buttons. - ui.style_mut().spacing.button_padding = egui::vec2(14.0, 0.0); - // Draw button to show integrated node info. View::item_button(ui, View::item_rounding(0, 1, true), CARET_RIGHT, None, || { AppConfig::toggle_show_connections_network_panel(); @@ -137,7 +205,7 @@ impl ConnectionsContent { Node::start(); }); } else if !Node::is_starting() && !Node::is_stopping() && !Node::is_restarting() { - // Draw button to open closed wallet. + // Draw button to stop integrated node. View::item_button(ui, Rounding::default(), POWER, Some(Colors::RED), || { Node::stop(false); }); diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index 0f876ff..1e36261 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -22,6 +22,7 @@ use crate::gui::views::{ConnectionsContent, NetworkMetrics, NetworkMining, Netwo use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; use crate::gui::views::types::{TitleContentType, TitleType}; use crate::node::Node; +use crate::tor::TorServer; use crate::wallet::ExternalConnection; /// Network content. @@ -147,7 +148,8 @@ impl NetworkContent { }); // Redraw after delay if node is syncing to update stats. - if Node::is_running() { + if Node::is_running() || TorServer::is_running() || TorServer::is_starting() || + TorServer::is_stopping() { ui.ctx().request_repaint_after(Node::STATS_UPDATE_DELAY); } } diff --git a/src/lib.rs b/src/lib.rs index b0519f5..2eb5772 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,7 +32,7 @@ i18n!("locales"); mod node; mod wallet; - +mod tor; mod settings; pub mod gui; @@ -63,8 +63,9 @@ fn android_main(app: AndroidApp) { let width = app.config().screen_width_dp().unwrap() as f32; let height = app.config().screen_height_dp().unwrap() as f32; + let size = egui::emath::vec2(width, height); let mut options = eframe::NativeOptions { - viewport: egui::ViewportBuilder::default().with_inner_size(vec2(width, height)), + viewport: egui::ViewportBuilder::default().with_inner_size(size), ..Default::default() }; // Setup limits that are guaranteed to be compatible with Android devices. diff --git a/src/settings/settings.rs b/src/settings/settings.rs index 0f8de64..67f2258 100644 --- a/src/settings/settings.rs +++ b/src/settings/settings.rs @@ -24,6 +24,7 @@ use serde::Serialize; use crate::node::NodeConfig; use crate::settings::AppConfig; +use crate::tor::TorServerConfig; use crate::wallet::ConnectionsConfig; lazy_static! { @@ -42,18 +43,27 @@ pub struct Settings { node_config: Arc>, /// Wallet connections configuration. conn_config: Arc>, + /// Tor server configuration. + tor_config: Arc> } impl Settings { /// Initialize settings with app and node configs. fn init() -> Self { + // Initialize app config. let app_config_path = Settings::get_config_path(AppConfig::FILE_NAME, None); let app_config = Self::init_config::(app_config_path); + + // Initialize tor config. + let tor_config_path = Settings::get_config_path(TorServerConfig::FILE_NAME, None); + let tor_config = Self::init_config::(tor_config_path); + let chain_type = &app_config.chain_type; Self { node_config: Arc::new(RwLock::new(NodeConfig::for_chain_type(chain_type))), conn_config: Arc::new(RwLock::new(ConnectionsConfig::for_chain_type(chain_type))), app_config: Arc::new(RwLock::new(app_config)), + tor_config: Arc::new(RwLock::new(tor_config)), } } @@ -99,6 +109,16 @@ impl Settings { SETTINGS_STATE.conn_config.write().unwrap() } + /// Get tor server configuration to read values. + pub fn tor_config_to_read() -> RwLockReadGuard<'static, TorServerConfig> { + SETTINGS_STATE.tor_config.read().unwrap() + } + + /// Get tor server configuration to update values. + pub fn tor_config_to_update() -> RwLockWriteGuard<'static, TorServerConfig> { + SETTINGS_STATE.tor_config.write().unwrap() + } + /// Get base directory path for configuration. pub fn get_base_path(sub_dir: Option) -> PathBuf { // Check if dir exists. diff --git a/src/tor/config.rs b/src/tor/config.rs new file mode 100644 index 0000000..490d082 --- /dev/null +++ b/src/tor/config.rs @@ -0,0 +1,56 @@ +// Copyright 2024 The Grim Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use serde_derive::{Deserialize, Serialize}; +use crate::Settings; + +/// Tor SOCKS proxy server configuration. +#[derive(Serialize, Deserialize, Clone)] +pub struct TorServerConfig { + socks_port: u16 +} + +/// Default SOCKS port value. +const DEFAULT_SOCKS_PORT: u16 = 9060; + +impl Default for TorServerConfig { + fn default() -> Self { + Self { + socks_port: DEFAULT_SOCKS_PORT, + } + } +} + +impl TorServerConfig { + /// Application configuration file name. + pub const FILE_NAME: &'static str = "app.toml"; + + /// Save application configuration to the file. + pub fn save(&self) { + Settings::write_to_file(self, Settings::get_config_path(Self::FILE_NAME, None)); + } + + /// Get SOCKS port value. + pub fn socks_port() -> u16 { + let r_config = Settings::tor_config_to_read(); + r_config.socks_port + } + + /// Save SOCKS port value. + pub fn save_socks_port(port: u16) { + let mut w_config = Settings::tor_config_to_update(); + w_config.socks_port = port; + w_config.save(); + } +} \ No newline at end of file diff --git a/src/tor/mod.rs b/src/tor/mod.rs new file mode 100644 index 0000000..6d3a498 --- /dev/null +++ b/src/tor/mod.rs @@ -0,0 +1,19 @@ +// Copyright 2024 The Grim Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod config; +pub use config::TorServerConfig; + +mod tor; +pub use tor::TorServer; \ No newline at end of file diff --git a/src/tor/tor.rs b/src/tor/tor.rs new file mode 100644 index 0000000..300f8bd --- /dev/null +++ b/src/tor/tor.rs @@ -0,0 +1,228 @@ +// Copyright 2024 The Grim Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::sync::{Arc, RwLock}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::thread; +use std::time::Duration; +use lazy_static::lazy_static; +use arti::socks::run_socks_proxy; +use arti_client::{TorClient, TorClientConfig}; +use arti_client::config::pt::{TransportConfigBuilder}; +use arti_client::config::{BridgeConfigBuilder, TorClientConfigBuilder, StorageConfigBuilder}; +use futures::task::SpawnExt; +use tokio::task::JoinHandle; +use anyhow::{Result}; +use tokio::time::sleep; +use tor_config::{CfgPath, Listen}; +use tor_rtcompat::{BlockOn, Runtime}; +use tor_rtcompat::tokio::TokioNativeTlsRuntime; + +use crate::tor::TorServerConfig; + +lazy_static! { + /// Static thread-aware state of [`Node`] to be updated from separate thread. + static ref TOR_SERVER_STATE: Arc = Arc::new(TorServer::default()); +} + +/// Tor SOCKS proxy server. +pub struct TorServer { + /// Flag to check if server is running. + running: AtomicBool, + /// Flag to check if server is starting. + starting: AtomicBool, + /// Flag to check if server needs to stop. + stopping: AtomicBool, + /// Flag to check if error happened. + error: AtomicBool, + /// Tor client to use for proxy. + client: Arc>>> +} + +impl Default for TorServer { + fn default() -> Self { + Self { + running: AtomicBool::new(false), + starting: AtomicBool::new(false), + stopping: AtomicBool::new(false), + error: AtomicBool::new(false), + client: Arc::new(RwLock::new(None)), + } + } +} + +impl TorServer { + /// Check if server is running. + pub fn is_running() -> bool { + TOR_SERVER_STATE.running.load(Ordering::Relaxed) + } + + /// Check if server is running. + pub fn is_starting() -> bool { + TOR_SERVER_STATE.starting.load(Ordering::Relaxed) + } + + /// Check if server is stopping. + pub fn is_stopping() -> bool { + TOR_SERVER_STATE.stopping.load(Ordering::Relaxed) + } + + /// Check if server has error. + pub fn has_error() -> bool { + TOR_SERVER_STATE.error.load(Ordering::Relaxed) + } + + /// Stop the server. + pub fn stop() { + TOR_SERVER_STATE.stopping.store(true, Ordering::Relaxed); + } + + /// Start or restart the server if already running. + pub fn start() { + if Self::is_running() { + Self::stop(); + } + + thread::spawn(|| { + while Self::is_stopping() { + thread::sleep(Duration::from_millis(1000)); + } + TOR_SERVER_STATE.starting.store(true, Ordering::Relaxed); + TOR_SERVER_STATE.error.store(false, Ordering::Relaxed); + + // Check if Tor client is already running. + if TOR_SERVER_STATE.client.read().unwrap().is_some() { + let r_client = TOR_SERVER_STATE.client.read().unwrap(); + let runtime = TokioNativeTlsRuntime::create().unwrap(); + let _ = runtime.clone().block_on( + Self::launch_socks_proxy(runtime, r_client.as_ref().unwrap().clone()) + ); + } else { + // Create Tor client config to connect. + let mut builder = TorClientConfig::builder(); + + // Setup Snowflake bridges. + Self::setup_bridges(&mut builder); + + // Create Tor client from config. + if let Ok(config) = builder.build() { + // Restart server on connection timeout. + thread::spawn(|| { + thread::sleep(Duration::from_millis(30000)); + let r_client = TOR_SERVER_STATE.client.read().unwrap(); + if r_client.is_none() { + Self::start(); + } + }); + // Create Tor client. + let runtime = TokioNativeTlsRuntime::create().unwrap(); + match TorClient::with_runtime(runtime.clone()) + .config(config) + .bootstrap_behavior(arti_client::BootstrapBehavior::OnDemand) + .create_unbootstrapped() { + Ok(tor_client) => { + let mut w_client = TOR_SERVER_STATE.client.write().unwrap(); + if w_client.is_some() { + return; + } + *w_client = Some(tor_client.clone()); + let _ = runtime.clone().block_on( + // Launch SOCKS proxy server. + Self::launch_socks_proxy(runtime, tor_client) + ); + } + Err(e) => { + eprintln!("{}", e); + TOR_SERVER_STATE.starting.store(false, Ordering::Relaxed); + TOR_SERVER_STATE.error.store(true, Ordering::Relaxed); + } + } + } else { + TOR_SERVER_STATE.starting.store(false, Ordering::Relaxed); + TOR_SERVER_STATE.error.store(true, Ordering::Relaxed); + } + } + }); + } + + /// Launch SOCKS proxy server. + async fn launch_socks_proxy(runtime: R, tor_client: TorClient) -> Result<()> { + let proxy_handle: JoinHandle> = tokio::spawn( + run_socks_proxy( + runtime, + tor_client, + Listen::new_localhost(TorServerConfig::socks_port()), + ) + ); + + // Setup server state flags. + TOR_SERVER_STATE.starting.store(false, Ordering::Relaxed); + TOR_SERVER_STATE.running.store(true, Ordering::Relaxed); + + loop { + if Self::is_stopping() || proxy_handle.is_finished() { + proxy_handle.abort(); + TOR_SERVER_STATE.stopping.store(false, Ordering::Relaxed); + TOR_SERVER_STATE.running.store(false, Ordering::Relaxed); + return Ok(()); + } + sleep(Duration::from_millis(3000)).await; + } + } + + /// Setup Tor Snowflake bridges. + fn setup_bridges(builder: &mut TorClientConfigBuilder) { + // Add a single bridge to the list of bridges, from a bridge line. + // This line comes from https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/blob/main/projects/common/bridges_list.snowflake.txt + // this is a real bridge line you can use as-is, after making sure it's still up to date with + // above link. + const BRIDGE1_LINE: &str = "Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=cdn.sstatic.net ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn"; + let bridge_1: BridgeConfigBuilder = BRIDGE1_LINE.parse().unwrap(); + builder.bridges().bridges().push(bridge_1); + + // Add a second bridge, built by hand. We use the 2nd bridge line from above, but modify some + // parameters to use AMP Cache instead of Fastly as a signaling channel. The difference in + // configuration is detailed in + // https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/tree/main/client#amp-cache + let mut bridge2_builder = BridgeConfigBuilder::default(); + bridge2_builder + .transport("snowflake") + .push_setting( + "fingerprint", + "8838024498816A039FCBBAB14E6F40A0843051FA" + ) + .push_setting("url", "https://snowflake-broker.torproject.net/") + .push_setting("ampcache", "https://cdn.ampproject.org/") + .push_setting("front", "www.google.com") + .push_setting( + "ice", + "stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.net:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478", + ) + .push_setting("utls-imitate", "hellorandomizedalpn"); + bridge2_builder.set_addrs(vec!["192.0.2.4:80".parse().unwrap()]); + bridge2_builder.set_ids(vec!["8838024498816A039FCBBAB14E6F40A0843051FA".parse().unwrap()]); + // Now insert the second bridge into our config builder. + builder.bridges().bridges().push(bridge2_builder); + + // Now configure an snowflake transport. (Requires the "pt-client" feature) + let mut transport = TransportConfigBuilder::default(); + transport + .protocols(vec!["snowflake".parse().unwrap()]) + // this might be named differently on some systems, this should work on Debian, + // but Archlinux is known to use `snowflake-pt-client` instead for instance. + .path(CfgPath::new("snowflake-client".into())) + .run_on_startup(true); + builder.bridges().transports().push(transport); + } +} \ No newline at end of file -- 2.39.5 From fc60d907d26f364877488502a8a8d6feb5a816d8 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Thu, 25 Apr 2024 15:15:10 +0300 Subject: [PATCH 33/46] node: delay after launch --- src/node/node.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/node/node.rs b/src/node/node.rs index b04cd3a..98453d7 100644 --- a/src/node/node.rs +++ b/src/node/node.rs @@ -585,6 +585,10 @@ fn start_node_server() -> Result { } let server_result = Server::new(server_config, None, api_chan); + + // Delay after server start. + thread::sleep(Duration::from_millis(5000)); + server_result } -- 2.39.5 From 5cf283696e8800724c9816c59acc539baa54c0e3 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Thu, 25 Apr 2024 15:18:48 +0300 Subject: [PATCH 34/46] wallet: delay for sync attempt --- src/wallet/wallet.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/wallet/wallet.rs b/src/wallet/wallet.rs index 112df1c..f00b768 100644 --- a/src/wallet/wallet.rs +++ b/src/wallet/wallet.rs @@ -790,6 +790,9 @@ impl Wallet { /// Delay in seconds to sync [`WalletData`] (60 seconds as average block time). const SYNC_DELAY: Duration = Duration::from_millis(60 * 1000); +/// Delay in seconds for sync thread to wait before start of new attempt. +const ATTEMPT_DELAY: Duration = Duration::from_millis(3 * 1000); + /// Number of attempts to sync [`WalletData`] before setting an error. const SYNC_ATTEMPTS: u8 = 10; @@ -836,7 +839,7 @@ fn start_sync(mut wallet: Wallet) -> Thread { // Skip cycle when node sync is not finished. if !Node::is_running() || Node::get_sync_status() != Some(SyncStatus::NoSync) { println!("integrated node wait"); - thread::park_timeout(Duration::from_millis(1000)); + thread::park_timeout(ATTEMPT_DELAY); continue; } } @@ -878,10 +881,10 @@ fn start_sync(mut wallet: Wallet) -> Thread { return; } - // Repeat after default delay or after 1 second if sync was not success. + // Repeat after default or attempt delay if synchronization was not successful. let delay = if wallet.sync_error() || wallet.get_sync_attempts() != 0 { - Duration::from_millis(1000) + ATTEMPT_DELAY } else { SYNC_DELAY }; -- 2.39.5 From 1e6376c497a6eb571ed65470c2f66e214da37ad8 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Thu, 25 Apr 2024 15:41:28 +0300 Subject: [PATCH 35/46] tor: fix restart --- src/tor/tor.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/tor/tor.rs b/src/tor/tor.rs index 300f8bd..928736b 100644 --- a/src/tor/tor.rs +++ b/src/tor/tor.rs @@ -104,10 +104,9 @@ impl TorServer { // Check if Tor client is already running. if TOR_SERVER_STATE.client.read().unwrap().is_some() { let r_client = TOR_SERVER_STATE.client.read().unwrap(); - let runtime = TokioNativeTlsRuntime::create().unwrap(); - let _ = runtime.clone().block_on( - Self::launch_socks_proxy(runtime, r_client.as_ref().unwrap().clone()) - ); + let client = r_client.as_ref().unwrap().clone(); + let runtime = client.runtime().clone(); + let _ = runtime.clone().block_on(Self::launch_socks_proxy(runtime, client)); } else { // Create Tor client config to connect. let mut builder = TorClientConfig::builder(); -- 2.39.5 From 744b7955c1f18dd15dd9c97203169739ba0d261c Mon Sep 17 00:00:00 2001 From: ardocrat Date: Sat, 27 Apr 2024 02:19:40 +0300 Subject: [PATCH 36/46] tor: launch services --- Cargo.lock | 161 ++++++++++++++++++++++++ Cargo.toml | 10 +- src/gui/views/network/connections.rs | 9 +- src/gui/views/network/content.rs | 8 +- src/tor/config.rs | 39 +++++- src/tor/tor.rs | 175 +++++++++++++++++++++++++-- 6 files changed, 379 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d429cb6..9c567fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -521,6 +521,7 @@ dependencies = [ "tor-guardmgr", "tor-hsclient", "tor-hscrypto", + "tor-hsservice", "tor-keymgr", "tor-linkspec", "tor-llcrypto", @@ -2966,6 +2967,27 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "fslock-arti-fork" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b21bd626aaab7b904b20bef6d9e06298914a0c8d9fb8b010483766b2e532791" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "fslock-guard" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9267d03223dd8877b0a3f8341661d21b7ba6a18e90f60e92e550addd30bc32c7" +dependencies = [ + "fslock-arti-fork", + "thiserror", + "winapi 0.3.9", +] + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -3394,7 +3416,9 @@ dependencies = [ "arti-client", "built", "chrono", + "curve25519-dalek 4.1.2", "dirs 5.0.1", + "ed25519-dalek 2.1.1", "eframe", "egui", "egui_extras", @@ -3425,12 +3449,18 @@ dependencies = [ "serde", "serde_derive", "serde_json", + "sha2 0.10.8", "sys-locale", "thiserror", "tokio 1.37.0", "tokio-util 0.7.10", "toml 0.8.12", "tor-config", + "tor-hscrypto", + "tor-hsrproxy", + "tor-hsservice", + "tor-keymgr", + "tor-llcrypto", "tor-rtcompat", "url", "wgpu", @@ -3872,6 +3902,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "growable-bloom-filter" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c669fa03050eb3445343f215d62fc1ab831e8098bc9a55f26e9724faff11075c" +dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "xxhash-rust", +] + [[package]] name = "h2" version = "0.2.7" @@ -4636,6 +4678,16 @@ dependencies = [ "serde_json", ] +[[package]] +name = "k12" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4dc5fdb62af2f520116927304f15d25b3c2667b4817b90efdc045194c912c54" +dependencies = [ + "digest 0.10.7", + "sha3 0.10.8", +] + [[package]] name = "keccak" version = "0.1.5" @@ -6528,6 +6580,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" +[[package]] +name = "rangemap" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" + [[package]] name = "rav1e" version = "0.7.1" @@ -7267,6 +7325,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_bytes" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.198" @@ -8397,6 +8464,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87bb9b74a5f5402768cee442147641d39ca2d0cba459f52fcca03cd8d978bd0d" dependencies = [ "caret", + "derive_builder_fork_arti", "derive_more", "digest 0.10.7", "thiserror", @@ -8724,6 +8792,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24b0c899ce91d6fe6461f646d1e3c8d421dd5c8b570c0799540c4d4a2de80013" dependencies = [ + "cipher 0.4.4", "data-encoding", "derive_more", "digest 0.10.7", @@ -8741,6 +8810,90 @@ dependencies = [ "tor-error", "tor-llcrypto", "tor-units", + "zeroize", +] + +[[package]] +name = "tor-hsrproxy" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e66cbbbff869500e673c775a056e0ead78152ebbfbe51ce442bd9833b873882" +dependencies = [ + "derive-adhoc 0.8.4", + "derive_builder_fork_arti", + "futures 0.3.30", + "rangemap", + "safelog", + "serde", + "serde_with", + "thiserror", + "tor-async-utils", + "tor-cell", + "tor-config", + "tor-error", + "tor-hsservice", + "tor-log-ratelim", + "tor-proto", + "tor-rtcompat", + "tracing", + "void", +] + +[[package]] +name = "tor-hsservice" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f81310c95fa1cd2d533dd1cf7ef43720308ac5bd0086801409491ebc1130c6e" +dependencies = [ + "amplify", + "async-trait", + "base64ct", + "derive-adhoc 0.8.4", + "derive_builder_fork_arti", + "derive_more", + "digest 0.10.7", + "educe", + "fs-mistrust", + "futures 0.3.30", + "growable-bloom-filter", + "hex", + "humantime 2.1.0", + "itertools 0.12.1", + "k12", + "once_cell", + "postage", + "rand 0.8.5", + "rand_core 0.6.4", + "retry-error", + "safelog", + "serde", + "serde_with", + "strum 0.26.2", + "thiserror", + "tor-async-utils", + "tor-basic-utils", + "tor-bytes", + "tor-cell", + "tor-cert", + "tor-circmgr", + "tor-config", + "tor-dirclient", + "tor-error", + "tor-hscrypto", + "tor-keymgr", + "tor-linkspec", + "tor-llcrypto", + "tor-log-ratelim", + "tor-netdir", + "tor-netdoc", + "tor-persist", + "tor-proto", + "tor-protover", + "tor-relay-selection", + "tor-rtcompat", + "tor-units", + "tracing", + "void", ] [[package]] @@ -8934,11 +9087,13 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7401ddb8c6a9ed71adeab421a20b786196409f02f389b415bb56041a5b0c80d4" dependencies = [ + "amplify", "derive-adhoc 0.8.4", "derive_more", "filetime", "fs-mistrust", "fslock", + "fslock-guard", "itertools 0.12.1", "paste", "sanitize-filename", @@ -10384,6 +10539,12 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" +[[package]] +name = "xxhash-rust" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927da81e25be1e1a2901d59b81b37dd2efd1fc9c9345a55007f09bf5a2d3ee03" + [[package]] name = "xz2" version = "0.1.7" diff --git a/Cargo.toml b/Cargo.toml index 5f7ecaf..98875e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,10 +56,18 @@ tokio = { version = "1.37.0", features = ["full"] } ## tor arti = { version = "1.2.0", features = ["experimental-api", "pt-client", "static"] } -arti-client = { version = "0.17.0", features = ["experimental-api", "pt-client", "static"] } +arti-client = { version = "0.17.0", features = ["experimental-api", "pt-client", "static", "onion-service-service"] } tor-rtcompat = { version = "0.17.0", features = ["static"] } tor-config = "0.17.0" fs-mistrust = "0.7.9" +tor-hsservice = "0.17.0" +tor-hsrproxy = "0.17.0" +tor-keymgr = "0.17.0" +ed25519-dalek = "2.1.1" +tor-llcrypto = "0.17.0" +tor-hscrypto = "0.17.0" +sha2 = "0.10.0" +curve25519-dalek = "4.1.2" ## stratum server tokio-util = { version = "0.7.8", features = ["codec"] } diff --git a/src/gui/views/network/connections.rs b/src/gui/views/network/connections.rs index 853b99f..b208879 100644 --- a/src/gui/views/network/connections.rs +++ b/src/gui/views/network/connections.rs @@ -12,8 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::time::Duration; use egui::{Align, Id, Layout, RichText, Rounding}; use url::Url; +use crate::tor::{TorServer, TorServerConfig}; use crate::AppConfig; use crate::gui::Colors; @@ -22,7 +24,6 @@ use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, NodeSetup, View}; use crate::gui::views::types::{ModalContainer, ModalPosition, TextEditOptions}; use crate::node::{Node, NodeConfig}; -use crate::tor::{TorServer, TorServerConfig}; use crate::wallet::{ConnectionsConfig, ExternalConnection}; /// Network connections content. @@ -122,6 +123,12 @@ impl ConnectionsContent { }); } } + + // Redraw after delay if Tor server is running. + if TorServer::is_running() || TorServer::is_starting() || + TorServer::is_stopping() { + ui.ctx().request_repaint_after(Duration::from_millis(1000)); + } } /// Draw Tor connection item content. diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index 1e36261..b248b10 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -16,13 +16,12 @@ use egui::{Margin, RichText, ScrollArea, Stroke}; use crate::AppConfig; use crate::gui::Colors; -use crate::gui::icons::{BRIEFCASE, CARDHOLDER, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE, PLUS_CIRCLE, POWER}; +use crate::gui::icons::{BRIEFCASE, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE, PLUS_CIRCLE, POWER}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{ConnectionsContent, NetworkMetrics, NetworkMining, NetworkNode, NetworkSettings, Root, TitlePanel, View}; use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; use crate::gui::views::types::{TitleContentType, TitleType}; use crate::node::Node; -use crate::tor::TorServer; use crate::wallet::ExternalConnection; /// Network content. @@ -148,8 +147,7 @@ impl NetworkContent { }); // Redraw after delay if node is syncing to update stats. - if Node::is_running() || TorServer::is_running() || TorServer::is_starting() || - TorServer::is_stopping() { + if Node::is_running() { ui.ctx().request_repaint_after(Node::STATS_UPDATE_DELAY); } } @@ -219,7 +217,7 @@ impl NetworkContent { self.connections.show_add_ext_conn_modal(None, cb); }); } - }, |ui, frame| { + }, |ui, _| { if !Root::is_dual_panel_mode(ui) { View::title_button(ui, BRIEFCASE, || { Root::toggle_network_panel(); diff --git a/src/tor/config.rs b/src/tor/config.rs index 490d082..2e16538 100644 --- a/src/tor/config.rs +++ b/src/tor/config.rs @@ -33,12 +33,45 @@ impl Default for TorServerConfig { } impl TorServerConfig { - /// Application configuration file name. - pub const FILE_NAME: &'static str = "app.toml"; + /// Tor configuration file name. + pub const FILE_NAME: &'static str = "tor.toml"; + + /// Directory for config and Tor related files. + const DIR_NAME: &'static str = "tor"; + + /// Subdirectory name for Tor state. + const STATE_SUB_DIR: &'static str = "state"; + /// Subdirectory name for Tor cache. + const CACHE_SUB_DIR: &'static str = "cache"; + /// Subdirectory name for Tor keystore. + const KEYSTORE_DIR: &'static str = "keystore"; /// Save application configuration to the file. pub fn save(&self) { - Settings::write_to_file(self, Settings::get_config_path(Self::FILE_NAME, None)); + Settings::write_to_file(self, Settings::get_config_path(Self::FILE_NAME, + Some(Self::DIR_NAME.to_string()))); + } + + /// Get subdirectory path from dir name. + fn sub_dir_path(name: &str) -> String { + let mut base = Settings::get_base_path(Some(Self::DIR_NAME.to_string())); + base.push(name); + base.to_str().unwrap().to_string() + } + + /// Get Tor state directory path. + pub fn state_path() -> String { + Self::sub_dir_path(Self::STATE_SUB_DIR) + } + + /// Get Tor cache directory path. + pub fn cache_path() -> String { + Self::sub_dir_path(Self::CACHE_SUB_DIR) + } + + /// Get Tor keystore directory path. + pub fn keystore_path() -> String { + Self::sub_dir_path(Self::KEYSTORE_DIR) } /// Get SOCKS port value. diff --git a/src/tor/tor.rs b/src/tor/tor.rs index 928736b..947264c 100644 --- a/src/tor/tor.rs +++ b/src/tor/tor.rs @@ -12,22 +12,37 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::collections::HashMap; +use std::net::SocketAddr; use std::sync::{Arc, RwLock}; use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; use std::time::Duration; use lazy_static::lazy_static; -use arti::socks::run_socks_proxy; -use arti_client::{TorClient, TorClientConfig}; -use arti_client::config::pt::{TransportConfigBuilder}; -use arti_client::config::{BridgeConfigBuilder, TorClientConfigBuilder, StorageConfigBuilder}; use futures::task::SpawnExt; use tokio::task::JoinHandle; -use anyhow::{Result}; +use anyhow::Result; use tokio::time::sleep; + +use arti::socks::run_socks_proxy; +use arti_client::{TorClient, TorClientConfig}; +use arti_client::config::pt::TransportConfigBuilder; +use arti_client::config::{BridgeConfigBuilder, TorClientConfigBuilder}; +use fs_mistrust::Mistrust; +use grin_util::secp::SecretKey; +use grin_wallet_util::OnionV3Address; +use ed25519_dalek::hazmat::ExpandedSecretKey; +use curve25519_dalek::digest::Digest; +use sha2::Sha512; use tor_config::{CfgPath, Listen}; -use tor_rtcompat::{BlockOn, Runtime}; -use tor_rtcompat::tokio::TokioNativeTlsRuntime; +use tor_rtcompat::{BlockOn, PreferredRuntime, Runtime}; +use tor_hsrproxy::OnionServiceReverseProxy; +use tor_hsrproxy::config::{Encapsulation, ProxyAction, ProxyPattern, ProxyRule, TargetAddr, ProxyConfigBuilder}; +use tor_hsservice::config::OnionServiceConfigBuilder; +use tor_hsservice::{HsIdKeypairSpecifier, HsIdPublicKeySpecifier, HsNickname}; +use tor_keymgr::{ArtiNativeKeystore, KeyMgrBuilder, KeystoreSelector}; +use tor_llcrypto::pk::ed25519::ExpandedKeypair; +use tor_hscrypto::pk::{HsIdKey, HsIdKeypair}; use crate::tor::TorServerConfig; @@ -36,18 +51,25 @@ lazy_static! { static ref TOR_SERVER_STATE: Arc = Arc::new(TorServer::default()); } -/// Tor SOCKS proxy server. +/// Tor server to use as SOCKS proxy for requests and to launch Onion services. pub struct TorServer { + /// Running Tor client. + client: Arc>>>, + /// Running Tor client configuration. + config: Arc>>, + /// Flag to check if server is running. running: AtomicBool, /// Flag to check if server is starting. starting: AtomicBool, /// Flag to check if server needs to stop. stopping: AtomicBool, + /// Flag to check if error happened. error: AtomicBool, - /// Tor client to use for proxy. - client: Arc>>> + + /// Mapping of running Onion services identifiers to proxy. + running_services: Arc>>> } impl Default for TorServer { @@ -58,6 +80,8 @@ impl Default for TorServer { stopping: AtomicBool::new(false), error: AtomicBool::new(false), client: Arc::new(RwLock::new(None)), + running_services: Arc::new(RwLock::new(HashMap::new())), + config: Arc::new(RwLock::new(None)), } } } @@ -109,13 +133,19 @@ impl TorServer { let _ = runtime.clone().block_on(Self::launch_socks_proxy(runtime, client)); } else { // Create Tor client config to connect. - let mut builder = TorClientConfig::builder(); + let mut builder = + TorClientConfigBuilder::from_directories(TorServerConfig::state_path(), + TorServerConfig::cache_path()); + builder.address_filter().allow_onion_addrs(true); // Setup Snowflake bridges. Self::setup_bridges(&mut builder); // Create Tor client from config. if let Ok(config) = builder.build() { + let mut w_config = TOR_SERVER_STATE.config.write().unwrap(); + *w_config = Some(config.clone()); + // Restart server on connection timeout. thread::spawn(|| { thread::sleep(Duration::from_millis(30000)); @@ -125,7 +155,7 @@ impl TorServer { } }); // Create Tor client. - let runtime = TokioNativeTlsRuntime::create().unwrap(); + let runtime = PreferredRuntime::current().unwrap(); match TorClient::with_runtime(runtime.clone()) .config(config) .bootstrap_behavior(arti_client::BootstrapBehavior::OnDemand) @@ -155,7 +185,7 @@ impl TorServer { }); } - /// Launch SOCKS proxy server. + /// Launch SOCKS proxy server to send connections. async fn launch_socks_proxy(runtime: R, tor_client: TorClient) -> Result<()> { let proxy_handle: JoinHandle> = tokio::spawn( run_socks_proxy( @@ -180,6 +210,125 @@ impl TorServer { } } + /// Check if Onion service is running. + pub fn is_service_running(id: &String) -> bool { + let r_services = TOR_SERVER_STATE.running_services.read().unwrap(); + r_services.contains_key(id) + } + + /// Stop running Onion service. + pub fn stop_service(id: &String) { + let mut w_services = TOR_SERVER_STATE.running_services.write().unwrap(); + if let Some(proxy) = w_services.remove(id) { + proxy.shutdown(); + } + } + + /// Run Onion service from listening local address, secret key and identifier. + pub fn run_service(addr: SocketAddr, key: SecretKey, id: &String) { + // Check if service is already running. + if Self::is_service_running(id) { + return; + } + + let hs_nickname = HsNickname::new(id.clone()).unwrap(); + let service_config = OnionServiceConfigBuilder::default() + .nickname(hs_nickname.clone()) + .build() + .unwrap(); + let r_client = TOR_SERVER_STATE.client.read().unwrap(); + let client = r_client.clone().unwrap(); + + // Add service key to keystore. + let r_config = TOR_SERVER_STATE.config.read().unwrap(); + let config = r_config.clone().unwrap(); + Self::add_service_key(config.fs_mistrust(), &key, &hs_nickname); + + // Launch Onion service. + let (_, request) = client.launch_onion_service(service_config).unwrap(); + + // Setup proxy to forward request from Tor address to local address. + let proxy_rule = ProxyRule::new( + ProxyPattern::one_port(80).unwrap(), + ProxyAction::Forward(Encapsulation::Simple, TargetAddr::Inet(addr)), + ); + let mut proxy_cfg_builder = ProxyConfigBuilder::default(); + proxy_cfg_builder.set_proxy_ports(vec![proxy_rule]); + let proxy = OnionServiceReverseProxy::new(proxy_cfg_builder.build().unwrap()); + + // Launch proxy at client runtime. + let proxy_service = proxy.clone(); + let runtime = client.runtime().clone(); + let nickname = hs_nickname.clone(); + client + .runtime() + .spawn(async move { + // Launch proxy for launched service. + match proxy_service.handle_requests(runtime, nickname.clone(), request).await { + Ok(()) => { + eprintln!("Onion service {} stopped.", nickname); + } + Err(e) => { + eprintln!("Onion service {} exited with an error: {}", nickname, e); + } + } + }).unwrap(); + + // Save running service. + let mut w_services = TOR_SERVER_STATE.running_services.write().unwrap(); + w_services.insert(id.clone(), proxy); + + let onion_addr = OnionV3Address::from_private(&key.0).unwrap(); + eprintln!("Onion service {} launched at {}", hs_nickname, onion_addr.to_ov3_str()); + } + + /// Add Onion service key to keystore. + fn add_service_key(mistrust: &Mistrust, key: &SecretKey, hs_nickname: &HsNickname) { + let mut client_config_builder = TorClientConfigBuilder::from_directories( + TorServerConfig::state_path(), + TorServerConfig::cache_path() + ); + client_config_builder + .address_filter() + .allow_onion_addrs(true); + let arti_store = + ArtiNativeKeystore::from_path_and_mistrust(TorServerConfig::keystore_path(), &mistrust) + .unwrap(); + + let key_manager = KeyMgrBuilder::default() + .default_store(Box::new(arti_store)) + .build() + .unwrap(); + + let expanded_sk = ExpandedSecretKey::from_bytes( + Sha512::default() + .chain_update(key) + .finalize() + .as_ref(), + ); + + let mut sk_bytes = [0_u8; 64]; + sk_bytes[0..32].copy_from_slice(&expanded_sk.scalar.to_bytes()); + sk_bytes[32..64].copy_from_slice(&expanded_sk.hash_prefix); + let expanded_kp = ExpandedKeypair::from_secret_key_bytes(sk_bytes).unwrap(); + + key_manager + .insert( + HsIdKey::from(expanded_kp.public().clone()), + &HsIdPublicKeySpecifier::new(hs_nickname.clone()), + KeystoreSelector::Default, + ) + .unwrap(); + + key_manager + .insert( + HsIdKeypair::from(expanded_kp), + &HsIdKeypairSpecifier::new(hs_nickname.clone()), + KeystoreSelector::Default, + ) + .unwrap(); + } + /// Setup Tor Snowflake bridges. fn setup_bridges(builder: &mut TorClientConfigBuilder) { // Add a single bridge to the list of bridges, from a bridge line. -- 2.39.5 From dbfb609f8d779d99a91d928c81726a930f4722bb Mon Sep 17 00:00:00 2001 From: ardocrat Date: Sat, 27 Apr 2024 02:30:00 +0300 Subject: [PATCH 37/46] build: update android-activity to 0.6.0 --- Cargo.lock | 60 ++++++++++++++++++++++++++++++++++++++++++++++-------- Cargo.toml | 6 +++--- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9c567fd..320a299 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -266,9 +266,30 @@ dependencies = [ "jni-sys", "libc", "log", - "ndk", + "ndk 0.8.0", "ndk-context", - "ndk-sys", + "ndk-sys 0.5.0+25.2.9519653", + "num_enum", + "thiserror", +] + +[[package]] +name = "android-activity" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" +dependencies = [ + "android-properties", + "bitflags 2.5.0", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk 0.9.0", + "ndk-context", + "ndk-sys 0.6.0+11769913", "num_enum", "thiserror", ] @@ -3408,7 +3429,7 @@ dependencies = [ name = "grim" version = "0.1.0" dependencies = [ - "android-activity", + "android-activity 0.6.0", "android_logger", "anyhow", "arboard", @@ -5266,13 +5287,27 @@ dependencies = [ "bitflags 2.5.0", "jni-sys", "log", - "ndk-sys", + "ndk-sys 0.5.0+25.2.9519653", "num_enum", "raw-window-handle 0.5.2", "raw-window-handle 0.6.1", "thiserror", ] +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.5.0", + "jni-sys", + "log", + "ndk-sys 0.6.0+11769913", + "num_enum", + "thiserror", +] + [[package]] name = "ndk-context" version = "0.1.1" @@ -5288,6 +5323,15 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + [[package]] name = "neli" version = "0.6.4" @@ -10003,7 +10047,7 @@ dependencies = [ "log", "metal", "naga", - "ndk-sys", + "ndk-sys 0.5.0+25.2.9519653", "objc", "once_cell", "parking_lot 0.12.1", @@ -10344,7 +10388,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca" dependencies = [ "ahash", - "android-activity", + "android-activity 0.5.2", "atomic-waker", "bitflags 2.5.0", "bytemuck", @@ -10358,8 +10402,8 @@ dependencies = [ "libc", "log", "memmap2", - "ndk", - "ndk-sys", + "ndk 0.8.0", + "ndk-sys 0.5.0+25.2.9519653", "objc2 0.4.1", "once_cell", "orbclient", diff --git a/Cargo.toml b/Cargo.toml index 98875e5..5f13005 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,7 +85,7 @@ image = "0.25.1" [target.'cfg(target_os = "android")'.dependencies] android_logger = "0.13.1" jni = "0.21.1" -android-activity = "0.5.2" +android-activity = { version = "0.6.0", features = ["game-activity"] } wgpu = "0.19" -winit = { version = "0.29", features = [ "android-game-activity" ] } -eframe = { version = "0.27.2", features = [ "wgpu", "android-game-activity" ] } \ No newline at end of file +winit = { version = "0.29", features = ["android-game-activity"] } +eframe = { version = "0.27.2", features = ["wgpu", "android-game-activity"] } \ No newline at end of file -- 2.39.5 From 1d9c8533adfbd34db8586b12f772a1360e0e111c Mon Sep 17 00:00:00 2001 From: ardocrat Date: Sat, 27 Apr 2024 03:01:29 +0300 Subject: [PATCH 38/46] tor: runtime --- src/tor/tor.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tor/tor.rs b/src/tor/tor.rs index 947264c..6e8771f 100644 --- a/src/tor/tor.rs +++ b/src/tor/tor.rs @@ -35,7 +35,8 @@ use ed25519_dalek::hazmat::ExpandedSecretKey; use curve25519_dalek::digest::Digest; use sha2::Sha512; use tor_config::{CfgPath, Listen}; -use tor_rtcompat::{BlockOn, PreferredRuntime, Runtime}; +use tor_rtcompat::tokio::TokioNativeTlsRuntime; +use tor_rtcompat::{BlockOn, Runtime}; use tor_hsrproxy::OnionServiceReverseProxy; use tor_hsrproxy::config::{Encapsulation, ProxyAction, ProxyPattern, ProxyRule, TargetAddr, ProxyConfigBuilder}; use tor_hsservice::config::OnionServiceConfigBuilder; @@ -54,7 +55,7 @@ lazy_static! { /// Tor server to use as SOCKS proxy for requests and to launch Onion services. pub struct TorServer { /// Running Tor client. - client: Arc>>>, + client: Arc>>>, /// Running Tor client configuration. config: Arc>>, @@ -155,7 +156,7 @@ impl TorServer { } }); // Create Tor client. - let runtime = PreferredRuntime::current().unwrap(); + let runtime = TokioNativeTlsRuntime::create().unwrap(); match TorClient::with_runtime(runtime.clone()) .config(config) .bootstrap_behavior(arti_client::BootstrapBehavior::OnDemand) -- 2.39.5 From 12650c94fd38c54b3831b80f760bdf4db84220f7 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Tue, 30 Apr 2024 18:15:03 +0300 Subject: [PATCH 39/46] tor: fix launch onion server, wallet tor service, send over tor --- Cargo.lock | 287 ++++++++++-- Cargo.toml | 14 +- locales/en.yml | 22 +- locales/ru.yml | 22 +- src/gui/views/network/connections.rs | 80 +--- src/gui/views/types.rs | 11 +- src/gui/views/views.rs | 11 +- src/gui/views/wallets/wallet/content.rs | 4 +- src/gui/views/wallets/wallet/messages.rs | 117 +++-- src/gui/views/wallets/wallet/transport.rs | 524 +++++++++++++++++++++- src/settings/settings.rs | 12 +- src/tor/config.rs | 41 +- src/tor/mod.rs | 4 +- src/tor/tor.rs | 415 +++++++---------- src/wallet/config.rs | 5 +- src/wallet/types.rs | 4 +- src/wallet/wallet.rs | 442 +++++++++++++----- 17 files changed, 1408 insertions(+), 607 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 320a299..e4afdd9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -319,7 +319,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c494134f746c14dc653a35a4ea5aca24ac368529da5370ecf41fe0341c35772f" dependencies = [ "android_log-sys", - "env_logger", + "env_logger 0.10.2", "log", "once_cell", ] @@ -504,7 +504,6 @@ dependencies = [ "tracing", "tracing-appender", "tracing-subscriber", - "visibility", "winapi 0.3.9", ] @@ -556,6 +555,25 @@ dependencies = [ "void", ] +[[package]] +name = "arti-hyper" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7105f9e5214f3447e6b80f93c819b8399e6bc2d61e6b8b025ec205bebe8c3f" +dependencies = [ + "anyhow", + "arti-client", + "educe", + "hyper 0.14.28", + "pin-project", + "thiserror", + "tls-api", + "tls-api-native-tls", + "tokio 1.37.0", + "tor-error", + "tor-rtcompat", +] + [[package]] name = "as-raw-xcb-connection" version = "1.0.1" @@ -869,6 +887,17 @@ dependencies = [ "zbus", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi 0.3.9", +] + [[package]] name = "autocfg" version = "0.1.8" @@ -2619,6 +2648,19 @@ dependencies = [ "syn 2.0.60", ] +[[package]] +name = "env_logger" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" +dependencies = [ + "atty", + "humantime 1.3.0", + "log", + "regex", + "termcolor", +] + [[package]] name = "env_logger" version = "0.10.2" @@ -3435,6 +3477,7 @@ dependencies = [ "arboard", "arti", "arti-client", + "arti-hyper", "built", "chrono", "curve25519-dalek 4.1.2", @@ -3443,7 +3486,7 @@ dependencies = [ "eframe", "egui", "egui_extras", - "env_logger", + "env_logger 0.10.2", "fs-mistrust", "futures 0.3.30", "grin_api", @@ -3459,6 +3502,7 @@ dependencies = [ "grin_wallet_impls", "grin_wallet_libwallet", "grin_wallet_util", + "hyper 0.14.28", "image 0.25.1", "jni", "lazy_static", @@ -3473,6 +3517,8 @@ dependencies = [ "sha2 0.10.8", "sys-locale", "thiserror", + "tls-api", + "tls-api-native-tls", "tokio 1.37.0", "tokio-util 0.7.10", "toml 0.8.12", @@ -3504,13 +3550,13 @@ dependencies = [ "grin_store", "grin_util", "http 0.2.12", - "hyper", + "hyper 0.13.10", "hyper-rustls 0.20.0", "hyper-timeout", "lazy_static", "log", "regex", - "ring", + "ring 0.16.20", "rustls 0.17.0", "serde", "serde_derive", @@ -3686,7 +3732,7 @@ dependencies = [ "grin_store", "grin_util", "http 0.2.12", - "hyper", + "hyper 0.13.10", "hyper-rustls 0.20.0", "lmdb-zero", "log", @@ -3760,7 +3806,7 @@ dependencies = [ "grin_wallet_util", "log", "rand 0.6.5", - "ring", + "ring 0.16.20", "serde", "serde_derive", "serde_json", @@ -3801,13 +3847,13 @@ dependencies = [ "grin_wallet_impls", "grin_wallet_libwallet", "grin_wallet_util", - "hyper", + "hyper 0.13.10", "lazy_static", "log", "prettytable-rs", "qr_code", "rand 0.7.3", - "ring", + "ring 0.16.20", "serde", "serde_derive", "serde_json", @@ -3845,7 +3891,7 @@ dependencies = [ "rand 0.6.5", "regex", "reqwest", - "ring", + "ring 0.16.20", "serde", "serde_derive", "serde_json", @@ -3955,6 +4001,25 @@ dependencies = [ "tracing-futures", ] +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes 1.6.0", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.2.6", + "slab", + "tokio 1.37.0", + "tokio-util 0.7.10", + "tracing", +] + [[package]] name = "half" version = "2.4.1" @@ -4026,6 +4091,15 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hermit-abi" version = "0.3.9" @@ -4153,6 +4227,17 @@ dependencies = [ "http 0.2.12", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes 1.6.0", + "http 0.2.12", + "pin-project-lite 0.2.14", +] + [[package]] name = "httparse" version = "1.8.0" @@ -4206,9 +4291,9 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.2.7", "http 0.2.12", - "http-body", + "http-body 0.3.1", "httparse", "httpdate 0.3.2", "itoa 0.4.8", @@ -4220,6 +4305,30 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes 1.6.0", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.26", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate 1.0.3", + "itoa 1.0.11", + "pin-project-lite 0.2.14", + "socket2 0.5.6", + "tokio 1.37.0", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper-rustls" version = "0.20.0" @@ -4229,13 +4338,13 @@ dependencies = [ "bytes 0.5.6", "ct-logs", "futures-util", - "hyper", + "hyper 0.13.10", "log", "rustls 0.17.0", "rustls-native-certs", "tokio 0.2.25", "tokio-rustls 0.13.1", - "webpki", + "webpki 0.21.4", ] [[package]] @@ -4246,12 +4355,12 @@ checksum = "37743cc83e8ee85eacfce90f2f4102030d9ff0a95244098d781e9bee4a90abb6" dependencies = [ "bytes 0.5.6", "futures-util", - "hyper", + "hyper 0.13.10", "log", "rustls 0.18.1", "tokio 0.2.25", "tokio-rustls 0.14.1", - "webpki", + "webpki 0.21.4", ] [[package]] @@ -4261,7 +4370,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d1f9b0b8258e3ef8f45928021d3ef14096c2b93b99e4b8cfcabf1f58ec84b0a" dependencies = [ "bytes 0.5.6", - "hyper", + "hyper 0.13.10", "tokio 0.2.25", "tokio-io-timeout", ] @@ -4273,7 +4382,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed" dependencies = [ "bytes 0.5.6", - "hyper", + "hyper 0.13.10", "native-tls", "tokio 0.2.25", "tokio-tls", @@ -4579,7 +4688,7 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "windows-sys 0.48.0", ] @@ -4605,7 +4714,7 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "windows-sys 0.52.0", ] @@ -5608,7 +5717,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] @@ -6001,6 +6110,17 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "pem" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" +dependencies = [ + "base64 0.13.1", + "once_cell", + "regex", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -6177,7 +6297,7 @@ checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3" dependencies = [ "cfg-if 1.0.0", "concurrent-queue", - "hermit-abi", + "hermit-abi 0.3.9", "pin-project-lite 0.2.14", "rustix 0.38.34", "tracing", @@ -6818,8 +6938,8 @@ dependencies = [ "futures-core", "futures-util", "http 0.2.12", - "http-body", - "hyper", + "http-body 0.3.1", + "hyper 0.13.10", "hyper-rustls 0.21.0", "hyper-tls", "ipnet", @@ -6881,11 +7001,26 @@ dependencies = [ "libc", "once_cell", "spin 0.5.2", - "untrusted", + "untrusted 0.7.1", "web-sys", "winapi 0.3.9", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "getrandom 0.2.14", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + [[package]] name = "ripemd160" version = "0.9.1" @@ -7082,9 +7217,9 @@ checksum = "c0d4a31f5d68413404705d6982529b0e11a9aacd4839d1d6222ee3b8cb4015e1" dependencies = [ "base64 0.11.0", "log", - "ring", + "ring 0.16.20", "sct", - "webpki", + "webpki 0.21.4", ] [[package]] @@ -7095,9 +7230,9 @@ checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81" dependencies = [ "base64 0.12.3", "log", - "ring", + "ring 0.16.20", "sct", - "webpki", + "webpki 0.21.4", ] [[package]] @@ -7210,8 +7345,8 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] @@ -8036,6 +8171,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "test-cert-gen" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345f92b7cac59507cdaba298c5493f7c40e2063d31f6fc621105183344d5d50a" +dependencies = [ + "once_cell", + "pem", + "tempfile", +] + [[package]] name = "thiserror" version = "1.0.59" @@ -8177,6 +8323,53 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tls-api" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d1b3dfb0a60da3e8a130c9f2432063d9979928a05c2b2cdcfc9fd05e4f53a3" +dependencies = [ + "anyhow", + "log", + "pem", + "tempfile", + "thiserror", + "tokio 1.37.0", + "void", + "webpki 0.22.4", +] + +[[package]] +name = "tls-api-native-tls" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b935bda2458120a5d2cea542013796fcf13937566580027f6a08f42a52206f7" +dependencies = [ + "anyhow", + "native-tls", + "thiserror", + "tls-api", + "tls-api-test", + "tokio 1.37.0", +] + +[[package]] +name = "tls-api-test" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df107843d725428d76bb159040fbae6d1524dcf25d5b24c56daa6b37ce9dbb5" +dependencies = [ + "anyhow", + "env_logger 0.5.13", + "log", + "pem", + "test-cert-gen", + "tls-api", + "tokio 1.37.0", + "untrusted 0.6.2", + "webpki 0.22.4", +] + [[package]] name = "tokio" version = "0.2.25" @@ -8261,7 +8454,7 @@ dependencies = [ "futures-core", "rustls 0.17.0", "tokio 0.2.25", - "webpki", + "webpki 0.21.4", ] [[package]] @@ -8273,7 +8466,7 @@ dependencies = [ "futures-core", "rustls 0.18.1", "tokio 0.2.25", - "webpki", + "webpki 0.21.4", ] [[package]] @@ -9592,12 +9785,24 @@ dependencies = [ "traitobject", ] +[[package]] +name = "untrusted" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f" + [[package]] name = "untrusted" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.0" @@ -9949,8 +10154,18 @@ version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", +] + +[[package]] +name = "webpki" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] @@ -9959,7 +10174,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f20dea7535251981a9670857150d571846545088359b28e4951d350bdaf179f" dependencies = [ - "webpki", + "webpki 0.21.4", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 5f13005..8a84cdb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,19 +55,23 @@ serde_json = "1.0.115" tokio = { version = "1.37.0", features = ["full"] } ## tor -arti = { version = "1.2.0", features = ["experimental-api", "pt-client", "static"] } -arti-client = { version = "0.17.0", features = ["experimental-api", "pt-client", "static", "onion-service-service"] } +arti = { version = "1.2.0", features = ["pt-client", "static"] } +arti-client = { version = "0.17.0", features = ["pt-client", "static", "onion-service-service"] } tor-rtcompat = { version = "0.17.0", features = ["static"] } tor-config = "0.17.0" fs-mistrust = "0.7.9" tor-hsservice = "0.17.0" tor-hsrproxy = "0.17.0" tor-keymgr = "0.17.0" -ed25519-dalek = "2.1.1" tor-llcrypto = "0.17.0" tor-hscrypto = "0.17.0" +arti-hyper = "0.17.0" sha2 = "0.10.0" +ed25519-dalek = "2.1.1" curve25519-dalek = "4.1.2" +hyper = { version = "0.14.28", features = ["full"] } +tls-api = "0.9.0" +tls-api-native-tls = "0.9.0" ## stratum server tokio-util = { version = "0.7.8", features = ["codec"] } @@ -86,6 +90,6 @@ image = "0.25.1" android_logger = "0.13.1" jni = "0.21.1" android-activity = { version = "0.6.0", features = ["game-activity"] } -wgpu = "0.19" -winit = { version = "0.29", features = ["android-game-activity"] } +wgpu = "0.19.1" +winit = { version = "0.29.4", features = ["android-game-activity"] } eframe = { version = "0.27.2", features = ["wgpu", "android-game-activity"] } \ No newline at end of file diff --git a/locales/en.yml b/locales/en.yml index 64aa8f8..5192cf6 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -15,6 +15,9 @@ id: Identifier kernel: Kernel settings: Settings language: Language +scan: Scan +qr_code: QR code +repeat: Repeat wallets: await_conf_amount: Awaiting confirmation await_fin_amount: Awaiting finalization @@ -109,6 +112,19 @@ wallets: receive: Receive settings: Wallet settings change_server_confirmation: To apply change of connection settings, you need to re-open your wallet. Reopen it now? +transport: + desc: 'Use transport to receive or send messages synchronously:' + tor_network: Tor network + connected: Connected + connecting: Connecting + disconnecting: Disconnecting + conn_error: Connection error + disconnected: Disconnected + receiver_address: 'Address of the receiver:' + incorrect_addr_err: 'Entered address is incorrect:' + tor_send_error: An error occurred during sending over Tor, make sure receiver is online, transaction was canceled. + tor_autorun_desc: Whether to launch Tor service on wallet opening to receive transactions synchronously. + tor_sending: Sending over Tor network: self: Network type: 'Network type:' @@ -126,12 +142,6 @@ network: available: Available not_available: Not available availability_check: Availability check - tor_network: Tor network - server_enabled: Server enabled - server_starting: Server is starting - server_stopping: Server is stopping - server_error: Server error - server_disabled: Server disabled sync_status: node_restarting: Node is restarting node_down: Node is down diff --git a/locales/ru.yml b/locales/ru.yml index 7e66a9f..b8adce0 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -15,6 +15,9 @@ id: Идентификатор kernel: Ядро settings: Настройки language: Язык +scan: Сканировать +qr_code: QR-код +repeat: Повторить wallets: await_conf_amount: Ожидает подтверждения await_fin_amount: Ожидает завершения @@ -109,6 +112,19 @@ wallets: receive: Получить settings: Настройки кошелька change_server_confirmation: Для применения изменения настроек соединения необходимо переоткрыть кошелёк. Переоткрыть его сейчас? +transport: + desc: 'Используйте транспорт для синхронного получения или отправки сообщений:' + tor_network: Сеть Tor + connected: Подключено + connecting: Подключение + disconnecting: Отключение + conn_error: Ошибка подключения + disconnected: Отключено + receiver_address: 'Адрес получателя:' + incorrect_addr_err: 'Введённый адрес неверен:' + tor_send_error: Во время отправки через Tor произошла ошибка, убедитесь, что получатель находится онлайн, транзакция была отменена. + tor_autorun_desc: Запускать ли Tor сервис при открытии кошелька для синхронного получения транзакций. + tor_sending: Отправка через Tor network: self: Сеть type: 'Тип сети:' @@ -126,12 +142,6 @@ network: available: Доступно not_available: Недоступно availability_check: Проверка доступности - tor_network: Сеть Tor - server_enabled: Сервер включен - server_starting: Сервер запускается - server_stopping: Сервер останавливается - server_error: Ошибка сервера - server_disabled: Сервер отключен sync_status: node_restarting: Узел перезапускается node_down: Узел выключен diff --git a/src/gui/views/network/connections.rs b/src/gui/views/network/connections.rs index b208879..9e79ed2 100644 --- a/src/gui/views/network/connections.rs +++ b/src/gui/views/network/connections.rs @@ -12,14 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::time::Duration; use egui::{Align, Id, Layout, RichText, Rounding}; use url::Url; -use crate::tor::{TorServer, TorServerConfig}; use crate::AppConfig; use crate::gui::Colors; -use crate::gui::icons::{CARET_RIGHT, CHECK_CIRCLE, COMPUTER_TOWER, DOTS_THREE_CIRCLE, GEAR_SIX, PENCIL, POWER, TRASH, WARNING_CIRCLE, X_CIRCLE}; +use crate::gui::icons::{CARET_RIGHT, CHECK_CIRCLE, COMPUTER_TOWER, DOTS_THREE_CIRCLE, PENCIL, POWER, TRASH, X_CIRCLE}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, NodeSetup, View}; use crate::gui::views::types::{ModalContainer, ModalPosition, TextEditOptions}; @@ -101,15 +99,6 @@ impl ConnectionsContent { // Show integrated node info content. Self::integrated_node_item_ui(ui); - // Show transport connections. - ui.add_space(6.0); - let transport_text = format!("{}:", t!("wallets.transport")); - ui.label(RichText::new(transport_text).size(16.0).color(Colors::GRAY)); - ui.add_space(6.0); - - // Show Tor SOCKS server. - Self::tor_transport_item_ui(ui); - // Show external connections. let ext_conn_list = ConnectionsConfig::ext_conn_list(); if !ext_conn_list.is_empty() { @@ -123,73 +112,6 @@ impl ConnectionsContent { }); } } - - // Redraw after delay if Tor server is running. - if TorServer::is_running() || TorServer::is_starting() || - TorServer::is_stopping() { - ui.ctx().request_repaint_after(Duration::from_millis(1000)); - } - } - - /// Draw Tor connection item content. - fn tor_transport_item_ui(ui: &mut egui::Ui) { - // Draw round background. - let mut rect = ui.available_rect_before_wrap(); - rect.set_height(78.0); - let rounding = View::item_rounding(0, 1, false); - ui.painter().rect(rect, rounding, Colors::FILL, View::ITEM_STROKE); - - ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { - // Draw button to show Tor connection settings. - View::item_button(ui, View::item_rounding(0, 1, true), GEAR_SIX, None, || { - AppConfig::toggle_show_connections_network_panel(); - }); - - // Draw buttons to stop or start Tor server. - if !TorServer::is_stopping() && !TorServer::is_starting() { - if TorServer::is_running() { - View::item_button(ui, Rounding::default(), POWER, Some(Colors::RED), || { - TorServer::stop(); - }); - } else { - View::item_button(ui, Rounding::default(), POWER, Some(Colors::GREEN), || { - TorServer::start(); - }); - } - } - - let layout_size = ui.available_size(); - ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { - ui.add_space(6.0); - ui.vertical(|ui| { - ui.add_space(3.0); - ui.label(RichText::new(t!("network.tor_network")) - .size(18.0) - .color(Colors::TITLE)); - - // Setup SOCKS server address. - let socks_port = TorServerConfig::socks_port(); - let addr_text = format!("{} http://127.0.0.1:{}", COMPUTER_TOWER, socks_port); - ui.label(RichText::new(addr_text).size(15.0).color(Colors::TEXT)); - ui.add_space(1.0); - - // Setup server status text. - let (status_icon, status_text) = if TorServer::has_error() { - (WARNING_CIRCLE, t!("network.server_error")) - } else if TorServer::is_starting() { - (DOTS_THREE_CIRCLE, t!("network.server_starting")) - } else if TorServer::is_stopping() { - (DOTS_THREE_CIRCLE, t!("network.server_stopping")) - } else if TorServer::is_running() { - (CHECK_CIRCLE, t!("network.server_enabled")) - } else { - (X_CIRCLE, t!("network.server_disabled")) - }; - let status_text = format!("{} {}", status_icon, status_text); - ui.label(RichText::new(status_text).size(15.0).color(Colors::GRAY)); - }) - }); - }); } /// Draw integrated node connection item content. diff --git a/src/gui/views/types.rs b/src/gui/views/types.rs index 202aab4..44eef18 100644 --- a/src/gui/views/types.rs +++ b/src/gui/views/types.rs @@ -83,7 +83,9 @@ pub struct TextEditOptions { /// Flag to show copy button. pub copy: bool, /// Flag to show paste button. - pub paste: bool + pub paste: bool, + /// Flag to show button to scan QR code into text. + pub scan_qr: bool } impl TextEditOptions { @@ -95,6 +97,7 @@ impl TextEditOptions { password: false, copy: false, paste: false, + scan_qr: false, } } @@ -127,4 +130,10 @@ impl TextEditOptions { self.paste = true; self } + + /// Show button to scan QR code to text. + pub fn scan_qr(mut self) -> Self { + self.scan_qr = true; + self + } } \ No newline at end of file diff --git a/src/gui/views/views.rs b/src/gui/views/views.rs index f6c6193..911c765 100644 --- a/src/gui/views/views.rs +++ b/src/gui/views/views.rs @@ -25,7 +25,7 @@ use egui::text::{LayoutJob, TextFormat}; use egui::text_edit::TextEditState; use crate::gui::Colors; -use crate::gui::icons::{CHECK_SQUARE, CLIPBOARD_TEXT, COPY, EYE, EYE_SLASH, SQUARE}; +use crate::gui::icons::{CHECK_SQUARE, CLIPBOARD_TEXT, COPY, EYE, EYE_SLASH, SCAN, SQUARE}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::types::TextEditOptions; @@ -346,6 +346,15 @@ impl View { ui.add_space(8.0); } + // Setup scan QR code button. + if options.paste { + let scan_icon = SCAN.to_string(); + View::button(ui, scan_icon, Colors::WHITE, || { + //TODO: open scanner + }); + ui.add_space(8.0); + } + let layout_size = ui.available_size(); ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { // Setup text edit size. diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index a875d1e..e63819c 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -19,7 +19,7 @@ use grin_core::core::amount_to_hr_string; use crate::AppConfig; use crate::gui::Colors; -use crate::gui::icons::{BRIDGE, CHAT_CIRCLE_TEXT, CHECK, CHECK_FAT, FOLDER_USER, GEAR_FINE, GRAPH, PACKAGE, PATH, POWER, QR_CODE, REPEAT, USERS_THREE}; +use crate::gui::icons::{BRIDGE, CHAT_CIRCLE_TEXT, CHECK, CHECK_FAT, FOLDER_USER, GEAR_FINE, GRAPH, PACKAGE, PATH, POWER, REPEAT, SCAN, USERS_THREE}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, Root, View}; use crate::gui::views::types::{ModalPosition, TextEditOptions}; @@ -176,7 +176,7 @@ impl WalletContent { ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { // Draw button to scan QR code. - View::item_button(ui, View::item_rounding(0, 2, true), QR_CODE, None, || { + View::item_button(ui, View::item_rounding(0, 2, true), SCAN, None, || { //TODO: Scan with QR code. }); diff --git a/src/gui/views/wallets/wallet/messages.rs b/src/gui/views/wallets/wallet/messages.rs index f67bf27..dd55051 100644 --- a/src/gui/views/wallets/wallet/messages.rs +++ b/src/gui/views/wallets/wallet/messages.rs @@ -53,9 +53,6 @@ impl MessageError { /// Slatepacks messages interaction tab content. pub struct WalletMessages { - /// Flag to check if send or invoice request was opened. - send_request: bool, - /// Slatepack message to create response message. message_edit: String, /// Parsed Slatepack message. @@ -67,15 +64,17 @@ pub struct WalletMessages { /// Flag to check if Dandelion is needed to finalize transaction. dandelion: bool, - /// Amount to send or receive. + /// Flag to check if send or invoice request was opened for [`Modal`]. + send_request: bool, + /// Amount to send or receive at [`Modal`]. amount_edit: String, - /// Generated Slatepack message as request to send or receive funds. + /// Generated Slatepack message as request to send or receive funds at [`Modal`]. request_edit: String, - /// Flag to check if there is an error happened on invoice creation. + /// Flag to check if there is an error happened on request creation at [`Modal`]. request_error: Option, } -/// Identifier for invoice amount [`Modal`]. +/// Identifier for amount input [`Modal`]. const AMOUNT_MODAL: &'static str = "amount_modal"; impl WalletMessages { @@ -127,7 +126,7 @@ impl WalletTab for WalletMessages { .show_inside(ui, |ui| { ScrollArea::vertical() .scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible) - .id_source(Id::from("wallet_manual").with(wallet.get_config().id)) + .id_source(Id::from("wallet_messages").with(wallet.get_config().id)) .auto_shrink([false; 2]) .show(ui, |ui| { ui.vertical_centered(|ui| { @@ -181,6 +180,54 @@ impl WalletMessages { } } + /// Draw creation of request to send or receive funds. + fn request_ui(&mut self, + ui: &mut egui::Ui, + cb: &dyn PlatformCallbacks) { + ui.label(RichText::new(t!("wallets.create_request_desc")) + .size(16.0) + .color(Colors::INACTIVE_TEXT)); + ui.add_space(7.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| { + // Draw send request creation button. + let send_text = format!("{} {}", UPLOAD_SIMPLE, t!("wallets.send")); + View::button(ui, send_text, Colors::BUTTON, || { + // Setup modal values. + self.send_request = true; + self.amount_edit = "".to_string(); + self.request_error = None; + // Show send amount modal. + Modal::new(AMOUNT_MODAL) + .position(ModalPosition::CenterTop) + .title(t!("wallets.send")) + .show(); + cb.show_keyboard(); + }); + }); + columns[1].vertical_centered_justified(|ui| { + // Draw invoice request creation button. + let receive_text = format!("{} {}", DOWNLOAD_SIMPLE, t!("wallets.receive")); + View::button(ui, receive_text, Colors::BUTTON, || { + // Setup modal values. + self.send_request = false; + self.amount_edit = "".to_string(); + self.request_error = None; + // Show receive amount modal. + Modal::new(AMOUNT_MODAL) + .position(ModalPosition::CenterTop) + .title(t!("wallets.receive")) + .show(); + cb.show_keyboard(); + }); + }); + }); + } + /// Draw Slatepack message input content. fn input_slatepack_ui(&mut self, ui: &mut egui::Ui, @@ -509,55 +556,7 @@ impl WalletMessages { } } - /// Draw creation of request to send or receive funds. - fn request_ui(&mut self, - ui: &mut egui::Ui, - cb: &dyn PlatformCallbacks) { - ui.label(RichText::new(t!("wallets.create_request_desc")) - .size(16.0) - .color(Colors::INACTIVE_TEXT)); - ui.add_space(7.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| { - // Draw send request creation button. - let send_text = format!("{} {}", UPLOAD_SIMPLE, t!("wallets.send")); - View::button(ui, send_text, Colors::BUTTON, || { - // Setup modal values. - self.send_request = true; - self.amount_edit = "".to_string(); - self.request_error = None; - // Show send amount modal. - Modal::new(AMOUNT_MODAL) - .position(ModalPosition::CenterTop) - .title(t!("wallets.send")) - .show(); - cb.show_keyboard(); - }); - }); - columns[1].vertical_centered_justified(|ui| { - // Draw invoice request creation button. - let receive_text = format!("{} {}", DOWNLOAD_SIMPLE, t!("wallets.receive")); - View::button(ui, receive_text, Colors::BUTTON, || { - // Setup modal values. - self.send_request = false; - self.amount_edit = "".to_string(); - self.request_error = None; - // Show receive amount modal. - Modal::new(AMOUNT_MODAL) - .position(ModalPosition::CenterTop) - .title(t!("wallets.receive")) - .show(); - cb.show_keyboard(); - }); - }); - }); - } - - /// Draw invoice amount [`Modal`] content. + /// Draw amount input [`Modal`] content to create invoice or request to send funds. fn amount_modal_ui(&mut self, ui: &mut egui::Ui, wallet: &mut Wallet, @@ -621,7 +620,7 @@ impl WalletMessages { } } - // Show invoice creation error. + // Show request creation error. if self.request_error.is_some() { ui.add_space(12.0); ui.vertical_centered(|ui| { @@ -658,7 +657,7 @@ impl WalletMessages { wallet.issue_invoice(a) }; match message { - Ok(message) => { + Ok((_, message)) => { self.request_edit = message; cb.hide_keyboard(); } diff --git a/src/gui/views/wallets/wallet/transport.rs b/src/gui/views/wallets/wallet/transport.rs index 53f2adf..d105bcb 100644 --- a/src/gui/views/wallets/wallet/transport.rs +++ b/src/gui/views/wallets/wallet/transport.rs @@ -12,17 +12,54 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::Margin; +use std::sync::{Arc, RwLock}; +use std::thread; +use egui::{Align, Id, Layout, Margin, RichText, Rounding, ScrollArea}; +use egui::scroll_area::ScrollBarVisibility; +use grin_core::core::{amount_from_hr_string, amount_to_hr_string}; +use grin_wallet_libwallet::SlatepackAddress; + use crate::gui::Colors; +use crate::gui::icons::{CHECK_CIRCLE, COMPUTER_TOWER, COPY, DOTS_THREE_CIRCLE, EXPORT, GEAR_SIX, GLOBE_SIMPLE, POWER, QR_CODE, UPLOAD, WARNING_CIRCLE, X_CIRCLE}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::View; +use crate::gui::views::{Modal, Root, View}; +use crate::gui::views::types::{ModalPosition, TextEditOptions}; use crate::gui::views::wallets::wallet::types::{WalletTab, WalletTabType}; use crate::gui::views::wallets::wallet::WalletContent; +use crate::tor::Tor; use crate::wallet::Wallet; -/// Sending tab content. -#[derive(Default)] -pub struct WalletTransport; +/// Wallet transport tab content. +pub struct WalletTransport { + /// Flag to check if transaction is sending over Tor to show progress at [`Modal`]. + tor_sending: Arc>, + /// Flag to check if error occurred during sending of transaction over Tor at [`Modal`]. + tor_send_error: Arc>, + /// Flag to check if transaction sent successfully over Tor [`Modal`]. + tor_success: Arc>, + /// Entered amount value for [`Modal`]. + amount_edit: String, + /// Entered address value for [`Modal`]. + address_edit: String, + /// Flag to check if entered address is incorrect at [`Modal`]. + address_error: bool, + /// Flag to check if [`Modal`] was just opened to focus on first field. + modal_just_opened: bool, +} + +impl Default for WalletTransport { + fn default() -> Self { + Self { + tor_sending: Arc::new(RwLock::new(false)), + tor_send_error: Arc::new(RwLock::new(false)), + tor_success: Arc::new(RwLock::new(false)), + amount_edit: "".to_string(), + address_edit: "".to_string(), + address_error: false, + modal_just_opened: false, + } + } +} impl WalletTab for WalletTransport { fn get_type(&self) -> WalletTabType { @@ -33,11 +70,14 @@ impl WalletTab for WalletTransport { ui: &mut egui::Ui, _: &mut eframe::Frame, wallet: &mut Wallet, - _: &dyn PlatformCallbacks) { + cb: &dyn PlatformCallbacks) { if WalletContent::sync_ui(ui, wallet) { return; } + // Show modal content for this ui container. + self.modal_content_ui(ui, wallet, cb); + // Show transport content panel. egui::CentralPanel::default() .frame(egui::Frame { @@ -52,14 +92,478 @@ impl WalletTab for WalletTransport { ..Default::default() }) .show_inside(ui, |ui| { - self.transport_ui(ui, wallet); + ScrollArea::vertical() + .scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible) + .id_source(Id::from("wallet_transport").with(wallet.get_config().id)) + .auto_shrink([false; 2]) + .show(ui, |ui| { + ui.vertical_centered(|ui| { + View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + self.ui(ui, wallet, cb); + }); + }); + }); }); } } -impl WalletTransport { - /// Draw transport content. - pub fn transport_ui(&self, ui: &mut egui::Ui, wallet: &mut Wallet) { +/// Identifier for [`Modal`] to send amount over Tor. +const SEND_TOR_MODAL: &'static str = "send_tor_modal"; +impl WalletTransport { + /// Draw wallet transport content. + pub fn ui(&mut self, ui: &mut egui::Ui, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) { + ui.add_space(3.0); + ui.label(RichText::new(t!("transport.desc")) + .size(16.0) + .color(Colors::INACTIVE_TEXT)); + ui.add_space(7.0); + + // Draw Tor content. + self.tor_ui(ui, wallet, cb); + } + + /// Draw [`Modal`] content for this ui container. + fn modal_content_ui(&mut self, + ui: &mut egui::Ui, + wallet: &mut Wallet, + cb: &dyn PlatformCallbacks) { + match Modal::opened() { + None => {} + Some(id) => { + match id { + SEND_TOR_MODAL => { + Modal::ui(ui.ctx(), |ui, modal| { + self.send_tor_modal_ui(ui, wallet, modal, cb); + }); + } + _ => {} + } + } + } + } + + /// Draw Tor transport content. + fn tor_ui(&mut self, ui: &mut egui::Ui, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) { + // Draw header content. + self.tor_header_ui(ui, wallet, cb); + + // Draw receive info content. + if wallet.slatepack_address().is_some() { + self.tor_receive_ui(ui, wallet, cb); + } + + // Draw send content. + self.tor_send_ui(ui, wallet, cb); + } + + /// Draw Tor transport header content. + fn tor_header_ui(&self, ui: &mut egui::Ui, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) { + // Setup layout size. + let mut rect = ui.available_rect_before_wrap(); + rect.set_height(78.0); + + // Draw round background. + let bg_rect = rect.clone(); + let item_rounding = View::item_rounding(0, 2, false); + ui.painter().rect(bg_rect, item_rounding, Colors::BUTTON, View::ITEM_STROKE); + + ui.vertical(|ui| { + ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { + // Draw button to setup Tor transport. + let button_rounding = View::item_rounding(0, 2, true); + View::item_button(ui, button_rounding, GEAR_SIX, None, || { + //TODO: tor settings + }); + + // Draw button to enable/disable Tor listener for current wallet. + let service_id = &wallet.identifier(); + if !Tor::is_service_running(service_id) && + wallet.foreign_api_port().is_some() { + View::item_button(ui, Rounding::default(), POWER, Some(Colors::GREEN), || { + if let Ok(key) = wallet.secret_key() { + Tor::start_service(wallet.foreign_api_port().unwrap(), key, service_id); + } + }); + } else if !Tor::is_service_starting(service_id) { + View::item_button(ui, Rounding::default(), POWER, Some(Colors::RED), || { + Tor::stop_service(service_id); + }); + } + + let layout_size = ui.available_size(); + ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { + ui.add_space(6.0); + ui.vertical(|ui| { + ui.add_space(3.0); + ui.label(RichText::new(t!("transport.tor_network")) + .size(18.0) + .color(Colors::TITLE)); + + // Setup wallet API address text. + let port = wallet.foreign_api_port().unwrap(); + let address_text = format!("{} http://127.0.0.1:{}", GLOBE_SIMPLE, port); + ui.label(RichText::new(address_text).size(15.0).color(Colors::TEXT)); + ui.add_space(1.0); + + // Setup Tor status text. + let is_running = Tor::is_service_running(service_id); + let is_starting = Tor::is_service_starting(service_id); + let has_error = Tor::is_service_failed(service_id); + let (icon, text) = if is_starting { + (DOTS_THREE_CIRCLE, t!("transport.connecting")) + } else if has_error { + (WARNING_CIRCLE, t!("transport.conn_error")) + } else if is_running { + (CHECK_CIRCLE, t!("transport.connected")) + } else { + (X_CIRCLE, t!("transport.disconnected")) + }; + let status_text = format!("{} {}", icon, text); + ui.label(RichText::new(status_text).size(15.0).color(Colors::GRAY)); + }); + }); + }); + }); + } + + /// Draw Tor send content. + fn tor_receive_ui(&self, ui: &mut egui::Ui, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) { + let slatepack_addr = wallet.slatepack_address().unwrap(); + let service_id = &wallet.identifier(); + + // Setup layout size. + let mut rect = ui.available_rect_before_wrap(); + rect.set_height(52.0); + + // Draw round background. + let bg_rect = rect.clone(); + let item_rounding = View::item_rounding(1, 3, false); + ui.painter().rect(bg_rect, item_rounding, Colors::BUTTON, View::ITEM_STROKE); + + ui.vertical(|ui| { + ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { + // Draw button to setup Tor transport. + let button_rounding = View::item_rounding(1, 3, true); + View::item_button(ui, button_rounding, QR_CODE, None, || { + //TODO: qr for address + }); + + // Show button to enable/disable Tor listener for current wallet. + View::item_button(ui, Rounding::default(), COPY, None, || { + cb.copy_string_to_buffer(slatepack_addr.clone()); + }); + + let layout_size = ui.available_size(); + ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { + ui.add_space(6.0); + ui.vertical(|ui| { + ui.add_space(3.0); + + // Show wallet Slatepack address. + let address_color = if Tor::is_service_starting(service_id) { + Colors::INACTIVE_TEXT + } else if Tor::is_service_running(service_id) { + Colors::GREEN + } else { + Colors::RED + }; + View::ellipsize_text(ui, slatepack_addr, 15.0, address_color); + + let address_label = format!("{} {}", + COMPUTER_TOWER, + t!("network_mining.address")); + ui.label(RichText::new(address_label).size(15.0).color(Colors::GRAY)); + }); + }); + }); + }); + } + + /// Draw Tor receive content. + fn tor_send_ui(&mut self, ui: &mut egui::Ui, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) { + // Setup layout size. + let mut rect = ui.available_rect_before_wrap(); + rect.set_height(55.0); + + // Draw round background. + let bg_rect = rect.clone(); + let item_rounding = View::item_rounding(1, 2, false); + ui.painter().rect(bg_rect, item_rounding, Colors::FILL, View::ITEM_STROKE); + + ui.vertical(|ui| { + ui.allocate_ui_with_layout(rect.size(), Layout::top_down(Align::Center), |ui| { + ui.add_space(7.0); + // Draw button to open sending modal. + let send_text = format!("{} {}", EXPORT, t!("wallets.send")); + View::button(ui, send_text, Colors::WHITE, || { + self.show_send_tor_modal(cb); + }); + }); + }); + } + + /// Show [`Modal`] to send over Tor. + fn show_send_tor_modal(&mut self, cb: &dyn PlatformCallbacks) { + // Setup modal values. + let mut w_send_err = self.tor_send_error.write().unwrap(); + *w_send_err = false; + let mut w_sending = self.tor_sending.write().unwrap(); + *w_sending = false; + let mut w_success = self.tor_success.write().unwrap(); + *w_success = false; + self.modal_just_opened = true; + self.amount_edit = "".to_string(); + self.address_edit = "".to_string(); + self.address_error = false; + // Show modal. + Modal::new(SEND_TOR_MODAL) + .position(ModalPosition::CenterTop) + .title(t!("wallets.send")) + .show(); + cb.show_keyboard(); + } + + /// Check if error occurred during sending over Tor at [`Modal`]. + fn has_tor_send_error(&self) -> bool { + let r_send_err = self.tor_send_error.read().unwrap(); + r_send_err.clone() + } + + /// Check if transaction is sending over Tor to show progress at [`Modal`]. + fn tor_sending(&self) -> bool { + let r_sending = self.tor_sending.read().unwrap(); + r_sending.clone() + } + + /// Check if transaction sent over Tor with success at [`Modal`]. + fn tor_success(&self) -> bool { + let r_success = self.tor_success.read().unwrap(); + r_success.clone() + } + + /// Draw amount input [`Modal`] content to send over Tor. + /// Draw amount input [`Modal`] content to send over Tor. + fn send_tor_modal_ui(&mut self, + ui: &mut egui::Ui, + wallet: &mut Wallet, + modal: &Modal, + cb: &dyn PlatformCallbacks) { + ui.add_space(6.0); + let has_send_err = self.has_tor_send_error(); + let sending = self.tor_sending(); + if !has_send_err && !sending { + ui.vertical_centered(|ui| { + let data = wallet.get_data().unwrap(); + let amount = amount_to_hr_string(data.info.amount_currently_spendable, true); + let enter_text = t!("wallets.enter_amount_send","amount" => amount); + ui.label(RichText::new(enter_text) + .size(17.0) + .color(Colors::GRAY)); + }); + ui.add_space(8.0); + + // Draw amount text edit. + let amount_edit_id = Id::from(modal.id).with("amount").with(wallet.get_config().id); + let mut amount_edit_opts = TextEditOptions::new(amount_edit_id).h_center().no_focus(); + let amount_edit_before = self.amount_edit.clone(); + if self.modal_just_opened { + self.modal_just_opened = false; + amount_edit_opts.focus = true; + } + View::text_edit(ui, cb, &mut self.amount_edit, amount_edit_opts); + ui.add_space(8.0); + + // Check value if input was changed. + if amount_edit_before != self.amount_edit { + if !self.amount_edit.is_empty() { + match amount_from_hr_string(self.amount_edit.as_str()) { + Ok(a) => { + if !self.amount_edit.contains(".") { + // To avoid input of several "0". + if a == 0 { + self.amount_edit = "0".to_string(); + return; + } + } else { + // Check input after ".". + let parts = self.amount_edit.split(".").collect::>(); + if parts.len() == 2 && parts[1].len() > 9 { + self.amount_edit = amount_edit_before; + return; + } + } + + // Do not input amount more than balance in sending. + let b = wallet.get_data().unwrap().info.amount_currently_spendable; + if b < a { + self.amount_edit = amount_edit_before; + } + } + Err(_) => { + self.amount_edit = amount_edit_before; + } + } + } + } + + // Show address error or input description. + ui.vertical_centered(|ui| { + if self.address_error { + ui.label(RichText::new(t!("transport.incorrect_addr_err")) + .size(17.0) + .color(Colors::RED)); + } else { + ui.label(RichText::new(t!("transport.receiver_address")) + .size(17.0) + .color(Colors::GRAY)); + } + }); + ui.add_space(8.0); + + // Draw address text edit. + let addr_edit_before = self.address_edit.clone(); + let address_edit_id = Id::from(modal.id).with("address").with(wallet.get_config().id); + let address_edit_opts = TextEditOptions::new(address_edit_id) + .paste() + .scan_qr() + .no_focus(); + View::text_edit(ui, cb, &mut self.address_edit, address_edit_opts); + ui.add_space(12.0); + + // Check value if input was changed. + if addr_edit_before != self.address_edit { + self.address_error = false; + } + + // 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| { + View::button(ui, t!("modal.cancel"), Colors::WHITE, || { + self.amount_edit = "".to_string(); + self.address_edit = "".to_string(); + cb.hide_keyboard(); + modal.close(); + }); + }); + columns[1].vertical_centered_justified(|ui| { + View::button(ui, t!("continue"), Colors::WHITE, || { + if self.amount_edit.is_empty() { + return; + } + + // Check entered address. + let addr_str = self.address_edit.as_str(); + if let Ok(addr) = SlatepackAddress::try_from(addr_str) { + // Parse amount and send over Tor. + if let Ok(a) = amount_from_hr_string(self.amount_edit.as_str()) { + cb.hide_keyboard(); + let mut w_sending = self.tor_sending.write().unwrap(); + *w_sending = true; + { + let send_error = self.tor_send_error.clone(); + let send_success = self.tor_success.clone(); + let mut wallet = wallet.clone(); + thread::spawn(move || { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() + .block_on(async { + if wallet.send_tor(a, &addr).await.is_some() { + let mut w_send_success + = send_success.write().unwrap(); + *w_send_success = true; + } else { + let mut w_send_error + = send_error.write().unwrap(); + *w_send_error = true; + } + }); + }); + } + } + } else { + self.address_error = true; + } + }); + }); + }); + ui.add_space(6.0); + } else if has_send_err { + ui.add_space(6.0); + ui.label(RichText::new(t!("transport.tor_send_error")) + .size(17.0) + .color(Colors::RED)); + 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| { + View::button(ui, t!("modal.cancel"), Colors::WHITE, || { + self.amount_edit = "".to_string(); + self.address_edit = "".to_string(); + cb.hide_keyboard(); + modal.close(); + }); + }); + columns[1].vertical_centered_justified(|ui| { + View::button(ui, t!("repeat"), Colors::WHITE, || { + // Parse amount and send over Tor. + if let Ok(a) = amount_from_hr_string(self.amount_edit.as_str()) { + let mut w_send_error = self.tor_send_error.write().unwrap(); + *w_send_error = false; + let mut w_sending = self.tor_sending.write().unwrap(); + *w_sending = true; + { + let addr_text = self.address_edit.clone(); + let send_error = self.tor_send_error.clone(); + let send_success = self.tor_success.clone(); + let mut wallet = wallet.clone(); + thread::spawn(move || { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() + .block_on(async { + let addr_str = addr_text.as_str(); + let addr = &SlatepackAddress::try_from(addr_str) + .unwrap(); + if wallet.send_tor(a, &addr).await.is_some() { + let mut w_send_success + = send_success.write().unwrap(); + *w_send_success = true; + } else { + let mut w_send_error + = send_error.write().unwrap(); + *w_send_error = true; + } + }); + }); + } + } + }); + }); + }); + } else { + ui.add_space(16.0); + ui.vertical_centered(|ui| { + View::small_loading_spinner(ui); + ui.add_space(12.0); + ui.label(RichText::new(t!("transport.tor_sending")) + .size(17.0) + .color(Colors::TEXT)); + }); + ui.add_space(10.0); + + // Close modal on success sending. + if self.tor_success() { + modal.close(); + } + } } } \ No newline at end of file diff --git a/src/settings/settings.rs b/src/settings/settings.rs index 67f2258..d63466e 100644 --- a/src/settings/settings.rs +++ b/src/settings/settings.rs @@ -24,7 +24,7 @@ use serde::Serialize; use crate::node::NodeConfig; use crate::settings::AppConfig; -use crate::tor::TorServerConfig; +use crate::tor::TorConfig; use crate::wallet::ConnectionsConfig; lazy_static! { @@ -44,7 +44,7 @@ pub struct Settings { /// Wallet connections configuration. conn_config: Arc>, /// Tor server configuration. - tor_config: Arc> + tor_config: Arc> } impl Settings { @@ -55,8 +55,8 @@ impl Settings { let app_config = Self::init_config::(app_config_path); // Initialize tor config. - let tor_config_path = Settings::get_config_path(TorServerConfig::FILE_NAME, None); - let tor_config = Self::init_config::(tor_config_path); + let tor_config_path = Settings::get_config_path(TorConfig::FILE_NAME, None); + let tor_config = Self::init_config::(tor_config_path); let chain_type = &app_config.chain_type; Self { @@ -110,12 +110,12 @@ impl Settings { } /// Get tor server configuration to read values. - pub fn tor_config_to_read() -> RwLockReadGuard<'static, TorServerConfig> { + pub fn tor_config_to_read() -> RwLockReadGuard<'static, TorConfig> { SETTINGS_STATE.tor_config.read().unwrap() } /// Get tor server configuration to update values. - pub fn tor_config_to_update() -> RwLockWriteGuard<'static, TorServerConfig> { + pub fn tor_config_to_update() -> RwLockWriteGuard<'static, TorConfig> { SETTINGS_STATE.tor_config.write().unwrap() } diff --git a/src/tor/config.rs b/src/tor/config.rs index 2e16538..97dad16 100644 --- a/src/tor/config.rs +++ b/src/tor/config.rs @@ -12,31 +12,30 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::path::PathBuf; use serde_derive::{Deserialize, Serialize}; use crate::Settings; -/// Tor SOCKS proxy server configuration. +/// Tor configuration. #[derive(Serialize, Deserialize, Clone)] -pub struct TorServerConfig { - socks_port: u16 +pub struct TorConfig { + // Flag to check if Tor bridges usage is needed. + pub(crate) use_bridges: Option } -/// Default SOCKS port value. -const DEFAULT_SOCKS_PORT: u16 = 9060; - -impl Default for TorServerConfig { +impl Default for TorConfig { fn default() -> Self { Self { - socks_port: DEFAULT_SOCKS_PORT, + use_bridges: Some(false) } } } -impl TorServerConfig { +impl TorConfig { /// Tor configuration file name. pub const FILE_NAME: &'static str = "tor.toml"; - /// Directory for config and Tor related files. + /// Directory for Tor data files. const DIR_NAME: &'static str = "tor"; /// Subdirectory name for Tor state. @@ -48,11 +47,10 @@ impl TorServerConfig { /// Save application configuration to the file. pub fn save(&self) { - Settings::write_to_file(self, Settings::get_config_path(Self::FILE_NAME, - Some(Self::DIR_NAME.to_string()))); + Settings::write_to_file(self, Settings::get_config_path(Self::FILE_NAME, None)); } - /// Get subdirectory path from dir name. + /// Get path from subdirectory name. fn sub_dir_path(name: &str) -> String { let mut base = Settings::get_base_path(Some(Self::DIR_NAME.to_string())); base.push(name); @@ -71,19 +69,8 @@ impl TorServerConfig { /// Get Tor keystore directory path. pub fn keystore_path() -> String { - Self::sub_dir_path(Self::KEYSTORE_DIR) - } - - /// Get SOCKS port value. - pub fn socks_port() -> u16 { - let r_config = Settings::tor_config_to_read(); - r_config.socks_port - } - - /// Save SOCKS port value. - pub fn save_socks_port(port: u16) { - let mut w_config = Settings::tor_config_to_update(); - w_config.socks_port = port; - w_config.save(); + let mut base = PathBuf::from(Self::state_path()); + base.push(Self::KEYSTORE_DIR); + base.to_str().unwrap().to_string() } } \ No newline at end of file diff --git a/src/tor/mod.rs b/src/tor/mod.rs index 6d3a498..5380d98 100644 --- a/src/tor/mod.rs +++ b/src/tor/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. mod config; -pub use config::TorServerConfig; +pub use config::TorConfig; mod tor; -pub use tor::TorServer; \ No newline at end of file +pub use tor::Tor; \ No newline at end of file diff --git a/src/tor/tor.rs b/src/tor/tor.rs index 6e8771f..571c893 100644 --- a/src/tor/tor.rs +++ b/src/tor/tor.rs @@ -12,203 +12,129 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::HashMap; -use std::net::SocketAddr; +use std::collections::{BTreeMap, BTreeSet}; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::sync::{Arc, RwLock}; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::thread; -use std::time::Duration; +use futures::executor::block_on; use lazy_static::lazy_static; use futures::task::SpawnExt; -use tokio::task::JoinHandle; -use anyhow::Result; -use tokio::time::sleep; -use arti::socks::run_socks_proxy; use arti_client::{TorClient, TorClientConfig}; -use arti_client::config::pt::TransportConfigBuilder; -use arti_client::config::{BridgeConfigBuilder, TorClientConfigBuilder}; +use arti_client::config::TorClientConfigBuilder; use fs_mistrust::Mistrust; use grin_util::secp::SecretKey; -use grin_wallet_util::OnionV3Address; use ed25519_dalek::hazmat::ExpandedSecretKey; use curve25519_dalek::digest::Digest; use sha2::Sha512; -use tor_config::{CfgPath, Listen}; use tor_rtcompat::tokio::TokioNativeTlsRuntime; -use tor_rtcompat::{BlockOn, Runtime}; +use tor_rtcompat::Runtime; use tor_hsrproxy::OnionServiceReverseProxy; use tor_hsrproxy::config::{Encapsulation, ProxyAction, ProxyPattern, ProxyRule, TargetAddr, ProxyConfigBuilder}; use tor_hsservice::config::OnionServiceConfigBuilder; -use tor_hsservice::{HsIdKeypairSpecifier, HsIdPublicKeySpecifier, HsNickname}; +use tor_hsservice::{HsIdKeypairSpecifier, HsIdPublicKeySpecifier, HsNickname, RunningOnionService}; use tor_keymgr::{ArtiNativeKeystore, KeyMgrBuilder, KeystoreSelector}; use tor_llcrypto::pk::ed25519::ExpandedKeypair; use tor_hscrypto::pk::{HsIdKey, HsIdKeypair}; +use arti_hyper::ArtiHttpConnector; +use futures::TryFutureExt; +use hyper::Body; +use tls_api::{TlsConnector as TlsConnectorTrait, TlsConnectorBuilder}; -use crate::tor::TorServerConfig; +// On aarch64-apple-darwin targets there is an issue with the native and rustls +// tls implementation so this makes it fall back to the openssl variant. +// +// https://gitlab.torproject.org/tpo/core/arti/-/issues/715 +#[cfg(not(all(target_vendor = "apple", target_arch = "aarch64")))] +use tls_api_native_tls::TlsConnector; +#[cfg(all(target_vendor = "apple", target_arch = "aarch64"))] +use tls_api_openssl::TlsConnector; + + +use crate::tor::TorConfig; lazy_static! { /// Static thread-aware state of [`Node`] to be updated from separate thread. - static ref TOR_SERVER_STATE: Arc = Arc::new(TorServer::default()); + static ref TOR_SERVER_STATE: Arc = Arc::new(Tor::default()); } /// Tor server to use as SOCKS proxy for requests and to launch Onion services. -pub struct TorServer { - /// Running Tor client. - client: Arc>>>, - /// Running Tor client configuration. - config: Arc>>, - - /// Flag to check if server is running. - running: AtomicBool, - /// Flag to check if server is starting. - starting: AtomicBool, - /// Flag to check if server needs to stop. - stopping: AtomicBool, - - /// Flag to check if error happened. - error: AtomicBool, +pub struct Tor { + /// [`TorClient`] used for connections with configuration. + client: Arc, TorClientConfig)>>, /// Mapping of running Onion services identifiers to proxy. - running_services: Arc>>> + running_services: Arc, Arc)>>>, + /// Starting Onion services identifiers. + starting_services: Arc>>, + /// Failed Onion services identifiers. + failed_services: Arc>> } -impl Default for TorServer { +impl Default for Tor { fn default() -> Self { + // Create Tor client config. + let mut builder = + TorClientConfigBuilder::from_directories(TorConfig::state_path(), + TorConfig::cache_path()); + builder.address_filter().allow_onion_addrs(true); + + // Create connected Tor client from config. + let runtime = TokioNativeTlsRuntime::create().unwrap(); + let config = builder.build().unwrap(); + let client = TorClient::with_runtime(runtime) + .config(config.clone()) + .create_unbootstrapped() + .unwrap(); Self { - running: AtomicBool::new(false), - starting: AtomicBool::new(false), - stopping: AtomicBool::new(false), - error: AtomicBool::new(false), - client: Arc::new(RwLock::new(None)), - running_services: Arc::new(RwLock::new(HashMap::new())), - config: Arc::new(RwLock::new(None)), + client: Arc::new(RwLock::new((client, config))), + running_services: Arc::new(RwLock::new(BTreeMap::new())), + starting_services: Arc::new(RwLock::new(BTreeSet::new())), + failed_services: Arc::new(RwLock::new(BTreeSet::new())) } } } -impl TorServer { - /// Check if server is running. - pub fn is_running() -> bool { - TOR_SERVER_STATE.running.load(Ordering::Relaxed) - } +impl Tor { + /// Send post request using Tor. + pub async fn post(body: String, url: String) -> Option { + // Bootstrap client. + let client_config = TOR_SERVER_STATE.client.read().unwrap(); + let client = client_config.0.clone(); + client.bootstrap().await.unwrap(); - /// Check if server is running. - pub fn is_starting() -> bool { - TOR_SERVER_STATE.starting.load(Ordering::Relaxed) - } + // Create http tor-powered client to post data. + let tls_connector = TlsConnector::builder().unwrap().build().unwrap(); + let tor_connector = ArtiHttpConnector::new(client, tls_connector); + let http = hyper::Client::builder().build::<_, Body>(tor_connector); + + // Create request. + let req = hyper::Request::builder() + .method(hyper::Method::POST) + .uri(url) + .body(Body::from(body)) + .unwrap(); - /// Check if server is stopping. - pub fn is_stopping() -> bool { - TOR_SERVER_STATE.stopping.load(Ordering::Relaxed) - } - - /// Check if server has error. - pub fn has_error() -> bool { - TOR_SERVER_STATE.error.load(Ordering::Relaxed) - } - - /// Stop the server. - pub fn stop() { - TOR_SERVER_STATE.stopping.store(true, Ordering::Relaxed); - } - - /// Start or restart the server if already running. - pub fn start() { - if Self::is_running() { - Self::stop(); - } - - thread::spawn(|| { - while Self::is_stopping() { - thread::sleep(Duration::from_millis(1000)); - } - TOR_SERVER_STATE.starting.store(true, Ordering::Relaxed); - TOR_SERVER_STATE.error.store(false, Ordering::Relaxed); - - // Check if Tor client is already running. - if TOR_SERVER_STATE.client.read().unwrap().is_some() { - let r_client = TOR_SERVER_STATE.client.read().unwrap(); - let client = r_client.as_ref().unwrap().clone(); - let runtime = client.runtime().clone(); - let _ = runtime.clone().block_on(Self::launch_socks_proxy(runtime, client)); - } else { - // Create Tor client config to connect. - let mut builder = - TorClientConfigBuilder::from_directories(TorServerConfig::state_path(), - TorServerConfig::cache_path()); - builder.address_filter().allow_onion_addrs(true); - - // Setup Snowflake bridges. - Self::setup_bridges(&mut builder); - - // Create Tor client from config. - if let Ok(config) = builder.build() { - let mut w_config = TOR_SERVER_STATE.config.write().unwrap(); - *w_config = Some(config.clone()); - - // Restart server on connection timeout. - thread::spawn(|| { - thread::sleep(Duration::from_millis(30000)); - let r_client = TOR_SERVER_STATE.client.read().unwrap(); - if r_client.is_none() { - Self::start(); - } - }); - // Create Tor client. - let runtime = TokioNativeTlsRuntime::create().unwrap(); - match TorClient::with_runtime(runtime.clone()) - .config(config) - .bootstrap_behavior(arti_client::BootstrapBehavior::OnDemand) - .create_unbootstrapped() { - Ok(tor_client) => { - let mut w_client = TOR_SERVER_STATE.client.write().unwrap(); - if w_client.is_some() { - return; - } - *w_client = Some(tor_client.clone()); - let _ = runtime.clone().block_on( - // Launch SOCKS proxy server. - Self::launch_socks_proxy(runtime, tor_client) - ); - } - Err(e) => { - eprintln!("{}", e); - TOR_SERVER_STATE.starting.store(false, Ordering::Relaxed); - TOR_SERVER_STATE.error.store(true, Ordering::Relaxed); - } - } - } else { - TOR_SERVER_STATE.starting.store(false, Ordering::Relaxed); - TOR_SERVER_STATE.error.store(true, Ordering::Relaxed); + // Send request. + let mut resp = None; + match http.request(req).await { + Ok(r) => { + match hyper::body::to_bytes(r).await { + Ok(raw) => { + resp = Some(String::from_utf8_lossy(&raw).to_string()) + }, + Err(_) => {}, } - } - }); + }, + Err(_) => {}, + } + resp } - /// Launch SOCKS proxy server to send connections. - async fn launch_socks_proxy(runtime: R, tor_client: TorClient) -> Result<()> { - let proxy_handle: JoinHandle> = tokio::spawn( - run_socks_proxy( - runtime, - tor_client, - Listen::new_localhost(TorServerConfig::socks_port()), - ) - ); - - // Setup server state flags. - TOR_SERVER_STATE.starting.store(false, Ordering::Relaxed); - TOR_SERVER_STATE.running.store(true, Ordering::Relaxed); - - loop { - if Self::is_stopping() || proxy_handle.is_finished() { - proxy_handle.abort(); - TOR_SERVER_STATE.stopping.store(false, Ordering::Relaxed); - TOR_SERVER_STATE.running.store(false, Ordering::Relaxed); - return Ok(()); - } - sleep(Duration::from_millis(3000)).await; - } + /// Check if Onion service is starting. + pub fn is_service_starting(id: &String) -> bool { + let r_services = TOR_SERVER_STATE.starting_services.read().unwrap(); + r_services.contains(id) } /// Check if Onion service is running. @@ -217,36 +143,80 @@ impl TorServer { r_services.contains_key(id) } + /// Check if Onion service failed on start. + pub fn is_service_failed(id: &String) -> bool { + let r_services = TOR_SERVER_STATE.failed_services.read().unwrap(); + r_services.contains(id) + } + /// Stop running Onion service. pub fn stop_service(id: &String) { let mut w_services = TOR_SERVER_STATE.running_services.write().unwrap(); - if let Some(proxy) = w_services.remove(id) { + if let Some((svc, proxy)) = w_services.remove(id) { proxy.shutdown(); + drop(svc); } } - /// Run Onion service from listening local address, secret key and identifier. - pub fn run_service(addr: SocketAddr, key: SecretKey, id: &String) { + /// Start Onion service from listening local port and [`SecretKey`]. + pub fn start_service(port: u16, key: SecretKey, id: &String) { // Check if service is already running. if Self::is_service_running(id) { return; + } else { + // Save starting service. + let mut w_services = TOR_SERVER_STATE.starting_services.write().unwrap(); + w_services.insert(id.clone()); + // Remove service from failed. + let mut w_services = TOR_SERVER_STATE.failed_services.write().unwrap(); + w_services.remove(id); } - let hs_nickname = HsNickname::new(id.clone()).unwrap(); - let service_config = OnionServiceConfigBuilder::default() - .nickname(hs_nickname.clone()) - .build() - .unwrap(); - let r_client = TOR_SERVER_STATE.client.read().unwrap(); - let client = r_client.clone().unwrap(); + let service_id = id.clone(); + let client_config = TOR_SERVER_STATE.client.read().unwrap(); + let client = client_config.0.clone(); + let config = client_config.1.clone(); + client.clone().runtime().spawn(async move { + // Add service key to keystore. + let hs_nickname = HsNickname::new(service_id.clone()).unwrap(); + Self::add_service_key(config.fs_mistrust(), &key, &hs_nickname); - // Add service key to keystore. - let r_config = TOR_SERVER_STATE.config.read().unwrap(); - let config = r_config.clone().unwrap(); - Self::add_service_key(config.fs_mistrust(), &key, &hs_nickname); + // Bootstrap client and launch Onion service. + client.bootstrap().await.unwrap(); + let service_config = OnionServiceConfigBuilder::default() + .nickname(hs_nickname.clone()) + .build() + .unwrap(); + let (service, request) = client.launch_onion_service(service_config).unwrap(); - // Launch Onion service. - let (_, request) = client.launch_onion_service(service_config).unwrap(); + // Launch service proxy. + let addr = SocketAddr::new(IpAddr::from(Ipv4Addr::LOCALHOST), port); + tokio::spawn( + Self::run_service_proxy(addr, client, service.clone(), request, hs_nickname.clone()) + ).await.unwrap(); + + println!( + "Onion service {} launched at: {}", + hs_nickname, + service.onion_name().unwrap().to_string() + ); + }).unwrap(); + } + + /// Launch Onion service proxy. + async fn run_service_proxy( + addr: SocketAddr, + client: TorClient, + service: Arc, + request: S, + nickname: HsNickname + ) + where + R: Runtime, + S: futures::Stream + Unpin + Send + 'static, + { + let id = nickname.to_string(); + let runtime = client.runtime().clone(); // Setup proxy to forward request from Tor address to local address. let proxy_rule = ProxyRule::new( @@ -257,43 +227,49 @@ impl TorServer { proxy_cfg_builder.set_proxy_ports(vec![proxy_rule]); let proxy = OnionServiceReverseProxy::new(proxy_cfg_builder.build().unwrap()); - // Launch proxy at client runtime. - let proxy_service = proxy.clone(); - let runtime = client.runtime().clone(); - let nickname = hs_nickname.clone(); + // Save running service. + let mut w_services = TOR_SERVER_STATE.running_services.write().unwrap(); + w_services.insert(id.clone(), (service.clone(), proxy.clone())); + + // Remove service from starting. + let mut w_services = TOR_SERVER_STATE.starting_services.write().unwrap(); + w_services.remove(&id); + + // Start proxy for launched service. client .runtime() .spawn(async move { - // Launch proxy for launched service. - match proxy_service.handle_requests(runtime, nickname.clone(), request).await { + match proxy + .handle_requests(runtime, nickname.clone(), request) + .await { Ok(()) => { - eprintln!("Onion service {} stopped.", nickname); + // Remove service from running. + let mut w_services = + TOR_SERVER_STATE.running_services.write().unwrap(); + w_services.remove(&id); + + println!("Onion service {} stopped.", nickname); } Err(e) => { + // Remove service from running. + let mut w_services = + TOR_SERVER_STATE.running_services.write().unwrap(); + w_services.remove(&id); + // Save failed service. + let mut w_services = + TOR_SERVER_STATE.failed_services.write().unwrap(); + w_services.insert(id); + eprintln!("Onion service {} exited with an error: {}", nickname, e); } } }).unwrap(); - - // Save running service. - let mut w_services = TOR_SERVER_STATE.running_services.write().unwrap(); - w_services.insert(id.clone(), proxy); - - let onion_addr = OnionV3Address::from_private(&key.0).unwrap(); - eprintln!("Onion service {} launched at {}", hs_nickname, onion_addr.to_ov3_str()); } - /// Add Onion service key to keystore. + /// Save Onion service key to keystore. fn add_service_key(mistrust: &Mistrust, key: &SecretKey, hs_nickname: &HsNickname) { - let mut client_config_builder = TorClientConfigBuilder::from_directories( - TorServerConfig::state_path(), - TorServerConfig::cache_path() - ); - client_config_builder - .address_filter() - .allow_onion_addrs(true); let arti_store = - ArtiNativeKeystore::from_path_and_mistrust(TorServerConfig::keystore_path(), &mistrust) + ArtiNativeKeystore::from_path_and_mistrust(TorConfig::keystore_path(), &mistrust) .unwrap(); let key_manager = KeyMgrBuilder::default() @@ -329,49 +305,4 @@ impl TorServer { ) .unwrap(); } - - /// Setup Tor Snowflake bridges. - fn setup_bridges(builder: &mut TorClientConfigBuilder) { - // Add a single bridge to the list of bridges, from a bridge line. - // This line comes from https://gitlab.torproject.org/tpo/applications/tor-browser-build/-/blob/main/projects/common/bridges_list.snowflake.txt - // this is a real bridge line you can use as-is, after making sure it's still up to date with - // above link. - const BRIDGE1_LINE: &str = "Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=cdn.sstatic.net ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn"; - let bridge_1: BridgeConfigBuilder = BRIDGE1_LINE.parse().unwrap(); - builder.bridges().bridges().push(bridge_1); - - // Add a second bridge, built by hand. We use the 2nd bridge line from above, but modify some - // parameters to use AMP Cache instead of Fastly as a signaling channel. The difference in - // configuration is detailed in - // https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/tree/main/client#amp-cache - let mut bridge2_builder = BridgeConfigBuilder::default(); - bridge2_builder - .transport("snowflake") - .push_setting( - "fingerprint", - "8838024498816A039FCBBAB14E6F40A0843051FA" - ) - .push_setting("url", "https://snowflake-broker.torproject.net/") - .push_setting("ampcache", "https://cdn.ampproject.org/") - .push_setting("front", "www.google.com") - .push_setting( - "ice", - "stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.net:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478", - ) - .push_setting("utls-imitate", "hellorandomizedalpn"); - bridge2_builder.set_addrs(vec!["192.0.2.4:80".parse().unwrap()]); - bridge2_builder.set_ids(vec!["8838024498816A039FCBBAB14E6F40A0843051FA".parse().unwrap()]); - // Now insert the second bridge into our config builder. - builder.bridges().bridges().push(bridge2_builder); - - // Now configure an snowflake transport. (Requires the "pt-client" feature) - let mut transport = TransportConfigBuilder::default(); - transport - .protocols(vec!["snowflake".parse().unwrap()]) - // this might be named differently on some systems, this should work on Debian, - // but Archlinux is known to use `snowflake-pt-client` instead for instance. - .path(CfgPath::new("snowflake-client".into())) - .run_on_startup(true); - builder.bridges().transports().push(transport); - } } \ No newline at end of file diff --git a/src/wallet/config.rs b/src/wallet/config.rs index 7d5686e..c303885 100644 --- a/src/wallet/config.rs +++ b/src/wallet/config.rs @@ -39,7 +39,9 @@ pub struct WalletConfig { /// Minimal amount of confirmations. pub min_confirmations: u64, /// Flag to use Dandelion to broadcast transactions. - pub use_dandelion: Option + pub use_dandelion: Option, + /// Flag to enable Tor listener on start. + pub enable_tor_listener: Option } /// Base wallets directory name. @@ -74,6 +76,7 @@ impl WalletConfig { }, min_confirmations: MIN_CONFIRMATIONS_DEFAULT, use_dandelion: Some(true), + enable_tor_listener: Some(true), }; Settings::write_to_file(&config, config_path); config diff --git a/src/wallet/types.rs b/src/wallet/types.rs index e7b1b66..7ad3d8c 100644 --- a/src/wallet/types.rs +++ b/src/wallet/types.rs @@ -147,6 +147,8 @@ pub struct WalletTransaction { pub data: TxLogEntry, /// Calculated transaction amount between debited and credited amount. pub amount: u64, + /// Flag to check if transaction is cancelling. + pub cancelling: bool, /// Flag to check if transaction is posting after finalization. pub posting: bool, /// Flag to check if transaction can be finalized based on Slatepack message state. @@ -158,7 +160,7 @@ pub struct WalletTransaction { impl WalletTransaction { /// Check if transaction can be cancelled. pub fn can_cancel(&self) -> bool { - !self.posting && !self.data.confirmed && + !self.cancelling && !self.posting && !self.data.confirmed && self.data.tx_type != TxLogEntryType::TxReceivedCancelled && self.data.tx_type != TxLogEntryType::TxSentCancelled } diff --git a/src/wallet/wallet.rs b/src/wallet/wallet.rs index f00b768..f3b6ccd 100644 --- a/src/wallet/wallet.rs +++ b/src/wallet/wallet.rs @@ -27,17 +27,21 @@ use grin_api::{ApiServer, Router}; use grin_chain::SyncStatus; use grin_core::global; use grin_keychain::{ExtKeychain, Identifier, Keychain}; -use grin_util::Mutex; +use grin_util::{Mutex, ToHex}; +use grin_util::secp::SecretKey; use grin_util::types::ZeroingString; use grin_wallet_api::Owner; use grin_wallet_controller::controller; use grin_wallet_controller::controller::ForeignAPIHandlerV2; use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl, HTTPNodeClient}; -use grin_wallet_libwallet::{Error, InitTxArgs, IssueInvoiceTxArgs, NodeClient, RetrieveTxQueryArgs, RetrieveTxQuerySortField, RetrieveTxQuerySortOrder, Slate, SlateState, StatusMessage, TxLogEntry, TxLogEntryType, WalletInst, WalletLCProvider}; +use grin_wallet_libwallet::{address, Error, InitTxArgs, IssueInvoiceTxArgs, NodeClient, RetrieveTxQueryArgs, RetrieveTxQuerySortField, RetrieveTxQuerySortOrder, Slate, SlatepackAddress, SlateState, SlateVersion, StatusMessage, TxLogEntry, TxLogEntryType, VersionedSlate, WalletInst, WalletLCProvider}; use grin_wallet_libwallet::api_impl::owner::{cancel_tx, retrieve_summary_info, retrieve_txs}; -use crate::AppConfig; +use grin_wallet_util::OnionV3Address; +use serde_json::{json, Value}; +use crate::AppConfig; use crate::node::{Node, NodeConfig}; +use crate::tor::Tor; use crate::wallet::{ConnectionsConfig, ExternalConnection, WalletConfig}; use crate::wallet::types::{ConnectionMethod, WalletAccount, WalletData, WalletInstance, WalletTransaction}; @@ -51,11 +55,14 @@ pub struct Wallet { /// [`WalletInstance`] external connection id applied after opening. instance_ext_conn_id: Arc, + /// Wallet Slatepack address to receive txs at transport. + slatepack_address: Arc>>, + /// Wallet sync thread. sync_thread: Arc>>, - /// Foreign API server. - foreign_api_server: Arc>>, + /// Running wallet foreign API server and port. + foreign_api_server: Arc>>, /// Flag to check if wallet reopening is needed. reopen: Arc, @@ -91,7 +98,7 @@ pub struct Wallet { /// Default Foreign API server host. const DEFAULT_FOREIGN_API_HOST: &str = "127.0.0.1"; /// Default Foreign API server port. -const DEFAULT_FOREIGN_API_PORT: u16 = 3421; +const DEFAULT_FOREIGN_API_PORT: u16 = 3415; impl Wallet { /// Create new [`Wallet`] instance with provided [`WalletConfig`]. @@ -100,6 +107,7 @@ impl Wallet { config: Arc::new(RwLock::new(config)), instance: None, instance_ext_conn_id: Arc::new(AtomicI64::new(0)), + slatepack_address: Arc::new(RwLock::new(None)), sync_thread: Arc::from(RwLock::new(None)), foreign_api_server: Arc::new(RwLock::new(None)), reopen: Arc::new(AtomicBool::new(false)), @@ -210,6 +218,35 @@ impl Wallet { Ok(w_inst.parent_key_id()) } + /// Get wallet [`SecretKey`] for transports. + pub fn secret_key(&self) -> Result { + let instance = self.instance.clone().unwrap(); + let mut w_lock = instance.lock(); + let lc = w_lock.lc_provider()?; + let w_inst = lc.wallet_inst()?; + let k = w_inst.keychain((&None).as_ref())?; + let parent_key_id = w_inst.parent_key_id(); + let sec_key = address::address_from_derivation_path(&k, &parent_key_id, 0) + .map_err(|e| Error::TorConfig(format!("{:?}", e)))?; + Ok(sec_key) + } + + /// Get unique opened wallet identifier, including current account. + pub fn identifier(&self) -> String { + let config = self.get_config(); + format!("wallet_{}_{}", config.id, config.account.to_hex()) + } + + /// Get Slatepack address to receive txs at transport. + pub fn slatepack_address(&self) -> Option { + let r_address = self.slatepack_address.read().unwrap(); + if r_address.is_some() { + let addr = r_address.clone().unwrap(); + return Some(addr) + } + None + } + /// Get wallet config. pub fn get_config(&self) -> WalletConfig { self.config.read().unwrap().clone() @@ -222,10 +259,23 @@ impl Wallet { w_config.save(); } + /// Check if start of Tor listener on wallet opening is needed. + pub fn auto_start_tor_listener(&self) -> bool { + let r_config = self.config.read().unwrap(); + r_config.enable_tor_listener.unwrap_or(true) + } + + /// Update start of Tor listener on wallet opening. + pub fn update_auto_start_tor_listener(&self, start: bool) { + let mut w_config = self.config.write().unwrap(); + w_config.enable_tor_listener = Some(start); + w_config.save(); + } + /// 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) + r_config.use_dandelion.unwrap_or(true) } /// Update usage of Dandelion to post transactions. @@ -267,36 +317,47 @@ impl Wallet { } // Open the wallet. - let instance = self.instance.clone().unwrap(); - let mut wallet_lock = instance.lock(); - let lc = wallet_lock.lc_provider()?; - match lc.open_wallet(None, ZeroingString::from(password), false, false) { - Ok(_) => { - // Reset an error on opening. - self.set_sync_error(false); - self.reset_sync_attempts(); + { + let instance = self.instance.clone().unwrap(); + let mut wallet_lock = instance.lock(); + let lc = wallet_lock.lc_provider()?; + match lc.open_wallet(None, ZeroingString::from(password), false, false) { + Ok(_) => { + // Reset an error on opening. + self.set_sync_error(false); + self.reset_sync_attempts(); - // Set current account. - let wallet_inst = lc.wallet_inst()?; - let label = self.get_config().account.to_owned(); - wallet_inst.set_parent_key_id_by_name(label.as_str())?; + // Set current account. + let wallet_inst = lc.wallet_inst()?; + let label = self.get_config().account.to_owned(); + wallet_inst.set_parent_key_id_by_name(label.as_str())?; - // Start new synchronization thread or wake up existing one. - let mut thread_w = self.sync_thread.write().unwrap(); - if thread_w.is_none() { - let thread = start_sync(self.clone()); - *thread_w = Some(thread); - } else { - println!("unfreeze thread"); - thread_w.clone().unwrap().unpark(); + // Start new synchronization thread or wake up existing one. + let mut thread_w = self.sync_thread.write().unwrap(); + if thread_w.is_none() { + let thread = start_sync(self.clone()); + *thread_w = Some(thread); + } else { + println!("unfreeze thread"); + thread_w.clone().unwrap().unpark(); + } + self.is_open.store(true, Ordering::Relaxed); + } + Err(e) => { + self.instance = None; + return Err(e) } - self.is_open.store(true, Ordering::Relaxed); - } - Err(e) => { - self.instance = None; - return Err(e) } } + + // Set slatepack address. + let mut api = Owner::new(self.instance.clone().unwrap(), None); + controller::owner_single_use(None, None, Some(&mut api), |api, m| { + let mut w_address = self.slatepack_address.write().unwrap(); + *w_address = Some(api.get_slatepack_address(m, 0)?.to_string()); + Ok(()) + })?; + Ok(()) } @@ -335,17 +396,21 @@ impl Wallet { // Close wallet at separate thread. let wallet_close = self.clone(); let instance = wallet_close.instance.clone().unwrap(); + let service_id = wallet_close.identifier(); thread::spawn(move || { - // Stop created API server. + // Stop running API server. let api_server_exists = { wallet_close.foreign_api_server.read().unwrap().is_some() }; if api_server_exists { - let mut api_server_w = wallet_close.foreign_api_server.write().unwrap(); - api_server_w.as_mut().unwrap().stop(); - *api_server_w = None; + let mut w_api_server = wallet_close.foreign_api_server.write().unwrap(); + w_api_server.as_mut().unwrap().0.stop(); + *w_api_server = None; } + // Stop running Tor service. + Tor::stop_service(&service_id); + // Close the wallet. Self::close_wallet(&instance); @@ -391,9 +456,16 @@ impl Wallet { let mut api = Owner::new(self.instance.clone().unwrap(), None); controller::owner_single_use(None, None, Some(&mut api), |api, m| { api.set_active_account(m, label)?; + // Set Slatepack address. + let mut w_address = self.slatepack_address.write().unwrap(); + *w_address = Some(api.get_slatepack_address(m, 0)?.to_string()); Ok(()) })?; + // Stop service from previous account. + let cur_service_id = self.identifier(); + Tor::stop_service(&cur_service_id); + // Save account label into config. let mut w_config = self.config.write().unwrap(); w_config.account = label.to_owned(); @@ -478,6 +550,16 @@ impl Wallet { } } + /// Get running Foreign API server port. + pub fn foreign_api_port(&self) -> Option { + let r_api = self.foreign_api_server.read().unwrap(); + if r_api.is_some() { + let api = r_api.as_ref().unwrap(); + return Some(api.1); + } + None + } + /// Parse Slatepack message into [`Slate`]. pub fn parse_slatepack(&self, message: &String) -> Result { let api = Owner::new(self.instance.clone().unwrap(), None); @@ -570,7 +652,7 @@ impl Wallet { } /// Initialize a transaction to send amount, return request for funds receiver. - pub fn send(&self, amount: u64) -> Result { + pub fn send(&self, amount: u64) -> Result<(Slate, String), Error> { let config = self.get_config(); let args = InitTxArgs { src_acct_name: Some(config.account), @@ -587,16 +669,102 @@ impl Wallet { api.tx_lock_outputs(None, &slate)?; // Create Slatepack message response. - let response = self.create_slatepack_message(&slate)?; + let message_resp = self.create_slatepack_message(&slate)?; // Sync wallet info. self.sync(); - Ok(response) + Ok((slate, message_resp)) + } + + /// Send amount to provided address with Tor transport. + pub async fn send_tor(&mut self, amount: u64, addr: &SlatepackAddress) -> Option { + // Initialize transaction. + let send_res = self.send(amount); + + if send_res.is_err() { + return None; + } + let slate = send_res.unwrap().0; + + // Function to cancel initialized tx in case of error. + let cancel_tx = || { + let instance = self.instance.clone().unwrap(); + let _ = cancel_tx(instance, None, &None, None, Some(slate.clone().id)); + }; + + // Initialize parameters. + let tor_addr = OnionV3Address::try_from(addr).unwrap().to_http_str(); + let url = format!("{}/v2/foreign", tor_addr); + let slate_send = VersionedSlate::into_version(slate.clone(), SlateVersion::V4).unwrap(); + let body = json!({ + "jsonrpc": "2.0", + "method": "receive_tx", + "id": 1, + "params": [ + slate_send, + null, + null + ] + }).to_string(); + + // Send request to receiver. + let req_res = Tor::post(body, url).await; + if req_res.is_none() { + cancel_tx(); + return None; + } + + // Parse response and finalize transaction. + let res: Value = serde_json::from_str(&req_res.unwrap()).unwrap(); + println!("Response: {}", res); + if res["error"] != json!(null) { + let report = format!( + "Posting transaction slate: Error: {}, Message: {}", + res["error"]["code"], res["error"]["message"] + ); + println!("{}", report); + cancel_tx(); + return None; + } + + let slate_value = res["result"]["Ok"].clone(); + println!("slate_value: {}", slate_value); + + let mut ret_slate = None; + match Slate::deserialize_upgrade(&serde_json::to_string(&slate_value).unwrap()) { + Ok(s) => { + let mut api = Owner::new(self.instance.clone().unwrap(), None); + controller::owner_single_use(None, None, Some(&mut api), |api, m| { + return if let Ok(slate) = api.finalize_tx(m, &s) { + ret_slate = Some(slate.clone()); + let result = api.post_tx(m, &slate, self.can_use_dandelion()); + match result { + Ok(_) => { + println!("Tx sent successfully", ); + Ok(()) + } + Err(e) => { + eprintln!("Tx sent fail: {}", e); + Err(e) + } + } + } else { + Err(Error::GenericError("TX finalization error".to_string())) + }; + }).unwrap(); + } + Err(_) => {} + }; + + if ret_slate.is_none() { + cancel_tx(); + } + ret_slate } /// Initialize an invoice transaction to receive amount, return request for funds sender. - pub fn issue_invoice(&self, amount: u64) -> Result { + pub fn issue_invoice(&self, amount: u64) -> Result<(Slate, String), Error> { let args = IssueInvoiceTxArgs { dest_acct_name: None, amount, @@ -606,12 +774,12 @@ 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.clone())?; // Sync wallet info. self.sync(); - Ok(response) + Ok((slate, response)) } /// Handle message from the invoice issuer to send founds, return response for funds receiver. @@ -699,27 +867,46 @@ impl Wallet { /// Cancel transaction. pub fn cancel(&mut self, id: u32) { - let instance = self.instance.clone().unwrap(); - let _ = cancel_tx(instance, None, &None, Some(id), None); - // Setup cancelling status, posting flag, and ability to finalize. - let mut w_data = self.data.write().unwrap(); - let mut data = w_data.clone().unwrap(); - let txs = data.txs.iter_mut().map(|tx| { - if tx.data.id == id { - tx.posting = false; - tx.can_finalize = false; - tx.data.tx_type = if tx.data.tx_type == TxLogEntryType::TxReceived { - TxLogEntryType::TxReceivedCancelled - } else { - TxLogEntryType::TxSentCancelled - }; - } - tx.clone() - }).collect::>(); - data.txs = txs; - *w_data = Some(data); - // Refresh wallet info to update statuses. - self.sync(); + // Setup cancelling status. + { + let mut w_data = self.data.write().unwrap(); + let mut data = w_data.clone().unwrap(); + let txs = data.txs.iter_mut().map(|tx| { + if tx.data.id == id { + tx.cancelling = true; + tx.can_finalize = false; + } + tx.clone() + }).collect::>(); + data.txs = txs; + *w_data = Some(data); + } + + let wallet = self.clone(); + thread::spawn(move || { + let instance = wallet.instance.clone().unwrap(); + let _ = cancel_tx(instance, None, &None, Some(id), None); + // Setup posting flag, and ability to finalize. + let mut w_data = wallet.data.write().unwrap(); + let mut data = w_data.clone().unwrap(); + let txs = data.txs.iter_mut().map(|tx| { + if tx.data.id == id { + tx.cancelling = false; + tx.posting = false; + tx.can_finalize = false; + tx.data.tx_type = if tx.data.tx_type == TxLogEntryType::TxReceived { + TxLogEntryType::TxReceivedCancelled + } else { + TxLogEntryType::TxSentCancelled + }; + } + tx.clone() + }).collect::>(); + data.txs = txs; + *w_data = Some(data); + // Refresh wallet info to update statuses. + wallet.sync(); + }); } /// Change wallet password. @@ -803,9 +990,8 @@ fn start_sync(mut wallet: Wallet) -> Thread { wallet.txs_sync_progress.store(0, Ordering::Relaxed); wallet.repair_progress.store(0, Ordering::Relaxed); - println!("create new thread"); thread::spawn(move || loop { - println!("start new cycle"); + println!("SYNC {}, attempts: {}", wallet.get_config().name, wallet.get_sync_attempts()); // Close wallet on chain type change. if wallet.get_config().chain_type != AppConfig::chain_type() { @@ -814,7 +1000,6 @@ fn start_sync(mut wallet: Wallet) -> Thread { // Stop syncing if wallet was closed. if !wallet.is_open() { - println!("finishing thread at start"); // Clear thread instance. let mut thread_w = wallet.sync_thread.write().unwrap(); *thread_w = None; @@ -822,7 +1007,6 @@ fn start_sync(mut wallet: Wallet) -> Thread { // Clear wallet info. let mut w_data = wallet.data.write().unwrap(); *w_data = None; - println!("finish at start complete"); return; } @@ -838,26 +1022,36 @@ fn start_sync(mut wallet: Wallet) -> Thread { wallet.set_sync_error(not_enabled); // Skip cycle when node sync is not finished. if !Node::is_running() || Node::get_sync_status() != Some(SyncStatus::NoSync) { - println!("integrated node wait"); thread::park_timeout(ATTEMPT_DELAY); continue; } } - // Start Foreign API listener if API server was not created. - let api_server_exists = { + // Start Foreign API listener if API server is not running. + let mut api_server_running = { wallet.foreign_api_server.read().unwrap().is_some() }; - if !api_server_exists { + if !api_server_running && wallet.is_open() { match start_api_server(&mut wallet) { Ok(api_server) => { let mut api_server_w = wallet.foreign_api_server.write().unwrap(); *api_server_w = Some(api_server); + api_server_running = true; } Err(_) => {} } } + // Start Tor service if API server is running and wallet is open. + if wallet.auto_start_tor_listener() && wallet.is_open() && api_server_running && + !Tor::is_service_running(&wallet.identifier()) { + let r_foreign_api = wallet.foreign_api_server.read().unwrap(); + let api = r_foreign_api.as_ref().unwrap(); + if let Ok(sec_key) = wallet.secret_key() { + Tor::start_service(api.1, sec_key, &wallet.identifier()); + } + } + // Scan outputs if repair is needed or sync data if there is no error. if !wallet.sync_error() { if wallet.is_repairing() { @@ -869,7 +1063,6 @@ fn start_sync(mut wallet: Wallet) -> Thread { // Stop sync if wallet was closed. if !wallet.is_open() { - println!("finishing thread after updating"); // Clear thread instance. let mut thread_w = wallet.sync_thread.write().unwrap(); *thread_w = None; @@ -877,73 +1070,37 @@ fn start_sync(mut wallet: Wallet) -> Thread { // Clear wallet info. let mut w_data = wallet.data.write().unwrap(); *w_data = None; - println!("finishing after updating complete"); return; } // Repeat after default or attempt delay if synchronization was not successful. - let delay = if wallet.sync_error() - || wallet.get_sync_attempts() != 0 { + let failed_sync = wallet.sync_error() || wallet.get_sync_attempts() != 0; + let delay = if failed_sync { ATTEMPT_DELAY } else { SYNC_DELAY }; - println!("park for {}", delay.as_millis()); + if failed_sync { + println!("SYNC {} failed, attempts: {}, wait {}ms", + wallet.get_config().name, + wallet.get_sync_attempts(), + delay.as_millis()); + } else { + println!("SYNC success for {}, wait {}ms", + wallet.get_config().name, + delay.as_millis()); + } thread::park_timeout(delay); }).thread().clone() } -/// Start Foreign API server to accept txs via Tor and receive mining rewards from Stratum server. -fn start_api_server(wallet: &mut Wallet) -> Result { - // Find free port. - let free_port = (DEFAULT_FOREIGN_API_PORT..).find(|port| { - return match TcpListener::bind((DEFAULT_FOREIGN_API_HOST, port.to_owned())) { - Ok(_) => { - let node_p2p_port = NodeConfig::get_p2p_port(); - let node_api_port = NodeConfig::get_api_ip_port().1; - port.to_string() != node_p2p_port && port.to_string() != node_api_port - }, - Err(_) => false - } - }).unwrap(); - - // Setup API server address. - let api_addr = format!("{}:{}", DEFAULT_FOREIGN_API_HOST, free_port); - - // Start Foreign API server thread. - let instance = wallet.instance.clone().unwrap(); - let api_handler_v2 = ForeignAPIHandlerV2::new(instance, - Arc::new(Mutex::new(None)), - false, - Mutex::new(None)); - let mut router = Router::new(); - router - .add_route("/v2/foreign", Arc::new(api_handler_v2)) - .map_err(|_| Error::GenericError("Router failed to add route".to_string()))?; - - let api_chan: &'static mut (oneshot::Sender<()>, oneshot::Receiver<()>) = - Box::leak(Box::new(oneshot::channel::<()>())); - - let mut apis = ApiServer::new(); - println!("Starting HTTP Foreign listener API server at {}.", api_addr); - let socket_addr: SocketAddr = api_addr.parse().unwrap(); - let _ = apis.start(socket_addr, router, None, api_chan) - .map_err(|_| Error::GenericError("API thread failed to start".to_string()))?; - - println!("HTTP Foreign listener started."); - Ok(apis) -} - /// Retrieve [`WalletData`] from node. fn sync_wallet_data(wallet: &Wallet) { - println!("SYNC start, attempts: {}", wallet.get_sync_attempts()); - let wallet_info = wallet.clone(); let (info_tx, info_rx) = mpsc::channel::(); // Update info sync progress at separate thread. thread::spawn(move || { while let Ok(m) = info_rx.recv() { - println!("SYNC INFO MESSAGE"); match m { StatusMessage::UpdatingOutputs(_) => {} StatusMessage::UpdatingTransactions(_) => {} @@ -990,7 +1147,6 @@ fn sync_wallet_data(wallet: &Wallet) { let (txs_tx, txs_rx) = mpsc::channel::(); thread::spawn(move || { while let Ok(m) = txs_rx.recv() { - println!("SYNC TXS MESSAGE"); match m { StatusMessage::UpdatingOutputs(_) => {} StatusMessage::UpdatingTransactions(_) => {} @@ -1116,6 +1272,7 @@ fn sync_wallet_data(wallet: &Wallet) { new_txs.push(WalletTransaction { data: tx.clone(), amount, + cancelling: false, posting, can_finalize, repost_height, @@ -1148,8 +1305,6 @@ fn sync_wallet_data(wallet: &Wallet) { wallet.increment_sync_attempts(); } - println!("SYNC cycle finished, attempts: {}", wallet.get_sync_attempts()); - // Set an error if maximum number of attempts was reached. if wallet.get_sync_attempts() >= SYNC_ATTEMPTS { wallet.reset_sync_attempts(); @@ -1157,6 +1312,47 @@ fn sync_wallet_data(wallet: &Wallet) { } } +/// Start Foreign API server to receive mining rewards from Stratum server. +fn start_api_server(wallet: &mut Wallet) -> Result<(ApiServer, u16), Error> { + // Find free port. + let free_port = (DEFAULT_FOREIGN_API_PORT..).find(|port| { + return match TcpListener::bind((DEFAULT_FOREIGN_API_HOST, port.to_owned())) { + Ok(_) => { + let node_p2p_port = NodeConfig::get_p2p_port(); + let node_api_port = NodeConfig::get_api_ip_port().1; + port.to_string() != node_p2p_port && port.to_string() != node_api_port + }, + Err(_) => false + } + }).unwrap(); + + // Setup API server address. + let api_addr = format!("{}:{}", DEFAULT_FOREIGN_API_HOST, free_port); + + // Start Foreign API server thread. + let instance = wallet.instance.clone().unwrap(); + let api_handler_v2 = ForeignAPIHandlerV2::new(instance, + Arc::new(Mutex::new(None)), + false, + Mutex::new(None)); + let mut router = Router::new(); + router + .add_route("/v2/foreign", Arc::new(api_handler_v2)) + .map_err(|_| Error::GenericError("Router failed to add route".to_string()))?; + + let api_chan: &'static mut (oneshot::Sender<()>, oneshot::Receiver<()>) = + Box::leak(Box::new(oneshot::channel::<()>())); + + let mut apis = ApiServer::new(); + println!("Starting HTTP Foreign listener API server at {}.", api_addr); + let socket_addr: SocketAddr = api_addr.parse().unwrap(); + let _ = apis.start(socket_addr, router, None, api_chan) + .map_err(|_| Error::GenericError("API thread failed to start".to_string()))?; + + println!("HTTP Foreign listener started."); + Ok((apis, free_port)) +} + /// Update wallet accounts data. fn update_accounts(wallet: &Wallet, current_height: u64, current_spendable: Option) { // Update only current account if list is not empty. -- 2.39.5 From c3864cb229effce410b2aa1317db81caa4164223 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Tue, 30 Apr 2024 23:28:49 +0300 Subject: [PATCH 40/46] tor: settings modal, save slatepack message after finalization --- locales/en.yml | 1 + locales/ru.yml | 1 + src/gui/views/wallets/wallet/transport.rs | 62 +++++++++++++++++++---- src/wallet/wallet.rs | 11 ++-- 4 files changed, 61 insertions(+), 14 deletions(-) diff --git a/locales/en.yml b/locales/en.yml index 5192cf6..20d7fbc 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -125,6 +125,7 @@ transport: tor_send_error: An error occurred during sending over Tor, make sure receiver is online, transaction was canceled. tor_autorun_desc: Whether to launch Tor service on wallet opening to receive transactions synchronously. tor_sending: Sending over Tor + tor_settings: Tor Settings network: self: Network type: 'Network type:' diff --git a/locales/ru.yml b/locales/ru.yml index b8adce0..df821e9 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -125,6 +125,7 @@ transport: tor_send_error: Во время отправки через Tor произошла ошибка, убедитесь, что получатель находится онлайн, транзакция была отменена. tor_autorun_desc: Запускать ли Tor сервис при открытии кошелька для синхронного получения транзакций. tor_sending: Отправка через Tor + tor_settings: Настройки Tor network: self: Сеть type: 'Тип сети:' diff --git a/src/gui/views/wallets/wallet/transport.rs b/src/gui/views/wallets/wallet/transport.rs index d105bcb..3a643d5 100644 --- a/src/gui/views/wallets/wallet/transport.rs +++ b/src/gui/views/wallets/wallet/transport.rs @@ -20,7 +20,7 @@ use grin_core::core::{amount_from_hr_string, amount_to_hr_string}; use grin_wallet_libwallet::SlatepackAddress; use crate::gui::Colors; -use crate::gui::icons::{CHECK_CIRCLE, COMPUTER_TOWER, COPY, DOTS_THREE_CIRCLE, EXPORT, GEAR_SIX, GLOBE_SIMPLE, POWER, QR_CODE, UPLOAD, WARNING_CIRCLE, X_CIRCLE}; +use crate::gui::icons::{CHECK_CIRCLE, COMPUTER_TOWER, COPY, DOTS_THREE_CIRCLE, EXPORT, GEAR_SIX, POWER, QR_CODE, WARNING_CIRCLE, X_CIRCLE}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, Root, View}; use crate::gui::views::types::{ModalPosition, TextEditOptions}; @@ -110,6 +110,9 @@ impl WalletTab for WalletTransport { /// Identifier for [`Modal`] to send amount over Tor. const SEND_TOR_MODAL: &'static str = "send_tor_modal"; +/// Identifier for [`Modal`] to setup Tor service. +const TOR_SETTINGS_MODAL: &'static str = "tor_settings_modal"; + impl WalletTransport { /// Draw wallet transport content. pub fn ui(&mut self, ui: &mut egui::Ui, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) { @@ -137,6 +140,11 @@ impl WalletTransport { self.send_tor_modal_ui(ui, wallet, modal, cb); }); } + TOR_SETTINGS_MODAL => { + Modal::ui(ui.ctx(), |ui, modal| { + self.tor_settings_modal_ui(ui, wallet, modal); + }); + } _ => {} } } @@ -146,7 +154,7 @@ impl WalletTransport { /// Draw Tor transport content. fn tor_ui(&mut self, ui: &mut egui::Ui, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) { // Draw header content. - self.tor_header_ui(ui, wallet, cb); + self.tor_header_ui(ui, wallet); // Draw receive info content. if wallet.slatepack_address().is_some() { @@ -154,11 +162,11 @@ impl WalletTransport { } // Draw send content. - self.tor_send_ui(ui, wallet, cb); + self.tor_send_ui(ui, cb); } /// Draw Tor transport header content. - fn tor_header_ui(&self, ui: &mut egui::Ui, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) { + fn tor_header_ui(&self, ui: &mut egui::Ui, wallet: &mut Wallet) { // Setup layout size. let mut rect = ui.available_rect_before_wrap(); rect.set_height(78.0); @@ -173,7 +181,11 @@ impl WalletTransport { // Draw button to setup Tor transport. let button_rounding = View::item_rounding(0, 2, true); View::item_button(ui, button_rounding, GEAR_SIX, None, || { - //TODO: tor settings + // Show modal. + Modal::new(TOR_SETTINGS_MODAL) + .position(ModalPosition::CenterTop) + .title(t!("transport.tor_settings")) + .show(); }); // Draw button to enable/disable Tor listener for current wallet. @@ -202,7 +214,9 @@ impl WalletTransport { // Setup wallet API address text. let port = wallet.foreign_api_port().unwrap(); - let address_text = format!("{} http://127.0.0.1:{}", GLOBE_SIMPLE, port); + let address_text = format!("{} http://127.0.0.1:{}", + COMPUTER_TOWER, + port); ui.label(RichText::new(address_text).size(15.0).color(Colors::TEXT)); ui.add_space(1.0); @@ -227,6 +241,29 @@ impl WalletTransport { }); } + /// Draw tor transport settings [`Modal`] content. + fn tor_settings_modal_ui(&self, ui: &mut egui::Ui, wallet: &mut Wallet, modal: &Modal) { + ui.add_space(6.0); + ui.vertical_centered(|ui| { + ui.label(RichText::new(t!("transport.tor_autorun_desc")) + .size(17.0) + .color(Colors::INACTIVE_TEXT)); + + // Show Tor service autorun checkbox. + let autorun = wallet.auto_start_tor_listener(); + View::checkbox(ui, autorun, t!("network.autorun"), || { + wallet.update_auto_start_tor_listener(!autorun); + }); + }); + ui.add_space(6.0); + ui.vertical_centered_justified(|ui| { + View::button(ui, t!("close"), Colors::WHITE, || { + modal.close(); + }); + ui.add_space(6.0); + }); + } + /// Draw Tor send content. fn tor_receive_ui(&self, ui: &mut egui::Ui, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) { let slatepack_addr = wallet.slatepack_address().unwrap(); @@ -281,7 +318,7 @@ impl WalletTransport { } /// Draw Tor receive content. - fn tor_send_ui(&mut self, ui: &mut egui::Ui, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) { + fn tor_send_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { // Setup layout size. let mut rect = ui.available_rect_before_wrap(); rect.set_height(55.0); @@ -494,9 +531,11 @@ impl WalletTransport { ui.add_space(6.0); } else if has_send_err { ui.add_space(6.0); - ui.label(RichText::new(t!("transport.tor_send_error")) - .size(17.0) - .color(Colors::RED)); + ui.vertical_centered(|ui| { + ui.label(RichText::new(t!("transport.tor_send_error")) + .size(17.0) + .color(Colors::RED)); + }); ui.add_space(12.0); // Setup spacing between buttons. @@ -549,6 +588,7 @@ impl WalletTransport { }); }); }); + ui.add_space(6.0); } else { ui.add_space(16.0); ui.vertical_centered(|ui| { @@ -556,7 +596,7 @@ impl WalletTransport { ui.add_space(12.0); ui.label(RichText::new(t!("transport.tor_sending")) .size(17.0) - .color(Colors::TEXT)); + .color(Colors::GRAY)); }); ui.add_space(10.0); diff --git a/src/wallet/wallet.rs b/src/wallet/wallet.rs index f3b6ccd..4f3dc48 100644 --- a/src/wallet/wallet.rs +++ b/src/wallet/wallet.rs @@ -728,17 +728,21 @@ impl Wallet { return None; } + // Slatepack message json value. let slate_value = res["result"]["Ok"].clone(); - println!("slate_value: {}", slate_value); let mut ret_slate = None; match Slate::deserialize_upgrade(&serde_json::to_string(&slate_value).unwrap()) { Ok(s) => { let mut api = Owner::new(self.instance.clone().unwrap(), None); controller::owner_single_use(None, None, Some(&mut api), |api, m| { + // Finalize transaction. return if let Ok(slate) = api.finalize_tx(m, &s) { ret_slate = Some(slate.clone()); - let result = api.post_tx(m, &slate, self.can_use_dandelion()); + // Save Slatepack message to file. + let _ = self.create_slatepack_message(&slate).unwrap_or("".to_string()); + // Post transaction to blockchain. + let result = self.post(&slate, self.can_use_dandelion()); match result { Ok(_) => { println!("Tx sent successfully", ); @@ -828,8 +832,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)?; - // Post transaction to blockchain. + // Save Slatepack message to file. let _ = self.create_slatepack_message(&slate)?; + // Post transaction to blockchain. let _ = self.post(&slate, dandelion); // Sync wallet info. self.sync(); -- 2.39.5 From f083546c7c28edc6ec0fb8751b41144d0253c800 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Wed, 1 May 2024 01:23:46 +0300 Subject: [PATCH 41/46] android: update notification less frequently --- .../main/java/mw/gri/android/BackgroundService.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/android/app/src/main/java/mw/gri/android/BackgroundService.java b/android/app/src/main/java/mw/gri/android/BackgroundService.java index 99f3892..ea6b910 100644 --- a/android/app/src/main/java/mw/gri/android/BackgroundService.java +++ b/android/app/src/main/java/mw/gri/android/BackgroundService.java @@ -21,6 +21,8 @@ public class BackgroundService extends Service { private static final int SYNC_STATUS_NOTIFICATION_ID = 1; private NotificationCompat.Builder mNotificationBuilder; + private String mNotificationContentText = ""; + private final Runnable mUpdateSyncStatus = new Runnable() { @Override public void run() { @@ -28,9 +30,13 @@ public class BackgroundService extends Service { return; } // Update sync status at notification. - mNotificationBuilder.setContentText(getSyncStatusText()); - NotificationManager manager = getSystemService(NotificationManager.class); - manager.notify(SYNC_STATUS_NOTIFICATION_ID, mNotificationBuilder.build()); + String syncStatusText = getSyncStatusText(); + if (!mNotificationContentText.equals(syncStatusText)) { + mNotificationContentText = syncStatusText; + mNotificationBuilder.setContentText(mNotificationContentText); + NotificationManager manager = getSystemService(NotificationManager.class); + manager.notify(SYNC_STATUS_NOTIFICATION_ID, mNotificationBuilder.build()); + } // Send broadcast to MainActivity if exit from the app is needed after node stop. if (exitAppAfterNodeStop()) { -- 2.39.5 From 36720bc6e2b880568085e9d981a8ca59fd48b9a2 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Wed, 1 May 2024 01:42:34 +0300 Subject: [PATCH 42/46] node: fix stats getting to avoid deadlock --- src/node/node.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/node/node.rs b/src/node/node.rs index 98453d7..568b83d 100644 --- a/src/node/node.rs +++ b/src/node/node.rs @@ -134,8 +134,8 @@ impl Node { } /// Get [`StratumServer`] statistics. - pub fn get_stratum_stats() -> grin_util::RwLockReadGuard<'static, StratumStats> { - NODE_STATE.stratum_stats.read() + pub fn get_stratum_stats() -> StratumStats { + NODE_STATE.stratum_stats.read().clone() } /// Stop [`StratumServer`]. @@ -169,8 +169,8 @@ impl Node { } /// Get node [`Server`] statistics. - pub fn get_stats() -> RwLockReadGuard<'static, Option> { - NODE_STATE.stats.read().unwrap() + pub fn get_stats() -> Option { + NODE_STATE.stats.read().unwrap().clone() } /// Check if [`Server`] is not syncing (disabled or just running after synchronization). -- 2.39.5 From 9ccb39cc5d10702f88bd6d319919b972333eb165 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Wed, 1 May 2024 02:54:24 +0300 Subject: [PATCH 43/46] android: integrated node warning --- locales/en.yml | 1 + locales/ru.yml | 1 + src/gui/views/root.rs | 45 +++++++++++++++++++++++++++++++++++++++--- src/settings/config.rs | 17 ++++++++++++++++ 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/locales/en.yml b/locales/en.yml index 20d7fbc..2619f46 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -143,6 +143,7 @@ network: available: Available not_available: Not available availability_check: Availability check + android_warning: Attention to Android users. To synchronize integrated node successfully, you must allow access to notifications and remove battery usage restrictions for the Grim application at system settings of your phone. This is necessary operation for correct work of application in the background. sync_status: node_restarting: Node is restarting node_down: Node is down diff --git a/locales/ru.yml b/locales/ru.yml index df821e9..6007c82 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -143,6 +143,7 @@ network: available: Доступно not_available: Недоступно availability_check: Проверка доступности + android_warning: Вниманию пользователей Android. Для успешной синхронизации встроенного узла необходимо разрешить доступ к уведомлениям и снять ограничения на использование батареи для приложения Grim в настройках телефона. Это необходимая операция для корректной работы приложения в фоне. sync_status: node_restarting: Узел перезапускается node_down: Узел выключен diff --git a/src/gui/views/root.rs b/src/gui/views/root.rs index fba6d4f..bd58025 100644 --- a/src/gui/views/root.rs +++ b/src/gui/views/root.rs @@ -38,10 +38,12 @@ pub struct Root { /// Check if app exit is allowed on close event of [`eframe::App`] implementation. pub(crate) exit_allowed: bool, - /// Flag to show exit progress at [`Modal`]. show_exit_progress: bool, + /// Flag to check if first it's first draw of content. + first_draw: bool, + /// List of allowed [`Modal`] ids for this [`ModalContainer`]. allowed_modal_ids: Vec<&'static str> } @@ -56,8 +58,10 @@ impl Default for Root { wallets: WalletsContent::default(), exit_allowed, show_exit_progress: false, + first_draw: true, allowed_modal_ids: vec![ - Self::EXIT_MODAL_ID + Self::EXIT_MODAL_ID, + Self::ANDROID_INTEGRATED_NODE_WARNING_MODAL ], } } @@ -75,6 +79,7 @@ impl ModalContainer for Root { _: &dyn PlatformCallbacks) { match modal.id { Self::EXIT_MODAL_ID => self.exit_modal_content(ui, modal), + Self::ANDROID_INTEGRATED_NODE_WARNING_MODAL => self.android_warning_modal_ui(ui, modal), _ => {} } } @@ -82,7 +87,10 @@ impl ModalContainer for Root { impl Root { /// Identifier for exit confirmation [`Modal`]. - pub const EXIT_MODAL_ID: &'static str = "exit_confirmation"; + pub const EXIT_MODAL_ID: &'static str = "exit_confirmation_modal"; + + /// Identifier for integrated node warning [`Modal`] on Android. + const ANDROID_INTEGRATED_NODE_WARNING_MODAL: &'static str = "android_node_warning_modal"; /// Default width of side panel at application UI. pub const SIDE_PANEL_WIDTH: f32 = 400.0; @@ -125,6 +133,19 @@ impl Root { self.wallets.ui(ui, frame, cb); }); }); + + // Show integrated node warning on Android if needed. + if self.first_draw && OperatingSystem::from_target_os() == OperatingSystem::Android && + AppConfig::android_integrated_node_warning_needed() { + Modal::new(Self::ANDROID_INTEGRATED_NODE_WARNING_MODAL) + .title(t!("network.node")) + .show(); + } + + // Setup first draw flag. + if self.first_draw { + self.first_draw = false; + } } /// Get [`NetworkContent`] panel state and width. @@ -232,4 +253,22 @@ impl Root { } } } + + /// Draw content for integrated node warning [`Modal`] on Android. + fn android_warning_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal) { + ui.add_space(6.0); + ui.vertical_centered(|ui| { + ui.label(RichText::new(t!("network.android_warning")) + .size(17.0) + .color(Colors::TEXT)); + }); + ui.add_space(8.0); + ui.vertical_centered_justified(|ui| { + View::button(ui, t!("continue"), Colors::WHITE, || { + AppConfig::show_android_integrated_node_warning(); + modal.close(); + }); + }); + ui.add_space(6.0); + } } \ No newline at end of file diff --git a/src/settings/config.rs b/src/settings/config.rs index f847919..a8958a7 100644 --- a/src/settings/config.rs +++ b/src/settings/config.rs @@ -26,6 +26,9 @@ pub struct AppConfig { /// Chain type for node and wallets. pub(crate) chain_type: ChainTypes, + /// Flag to check if Android integrated node warning was shown. + android_integrated_node_warning: Option, + /// Flag to show wallet list at dual panel wallets mode. show_wallets_at_dual_panel: bool, /// Flag to show all connections at network panel or integrated node info. @@ -51,6 +54,7 @@ impl Default for AppConfig { Self { auto_start_node: false, chain_type: ChainTypes::default(), + android_integrated_node_warning: None, show_wallets_at_dual_panel: false, show_connections_network_panel: false, width: DEFAULT_WIDTH, @@ -193,4 +197,17 @@ impl AppConfig { } None } + + /// Check if integrated node warning is needed for Android. + pub fn android_integrated_node_warning_needed() -> bool { + let r_config = Settings::app_config_to_read(); + r_config.android_integrated_node_warning.unwrap_or(true) + } + + /// Mark integrated node warning for Android as shown. + pub fn show_android_integrated_node_warning() { + let mut w_app_config = Settings::app_config_to_update(); + w_app_config.android_integrated_node_warning = Some(false); + w_app_config.save(); + } } \ No newline at end of file -- 2.39.5 From 476f8afe169f2b00cc7bb14ba53253e0380f6fe7 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Wed, 1 May 2024 15:25:00 +0300 Subject: [PATCH 44/46] de: add missing translation keys --- locales/de.yml | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/locales/de.yml b/locales/de.yml index 0f149b1..143a4ed 100644 --- a/locales/de.yml +++ b/locales/de.yml @@ -1,3 +1,4 @@ +lang_name: copy: Kopieren paste: Einfügen continue: Weiter @@ -10,6 +11,13 @@ show: Zeigen delete: Löschen clear: Clear create: Erstellen +id: +kernel: +settings: +language: +scan: +qr_code: +repeat: wallets: await_conf_amount: Erwarte Bestätigung await_fin_amount: Warten auf die Fertigstellung @@ -49,12 +57,12 @@ wallets: closing: Schließen checking: Überprüfung default_wallet: Standard-Wallet + new_account_desc: wallet_loading: Wallet wird geladen wallet_closing: Wallet schließen wallet_checking: Wallet prüfen tx_loading: Laden von Transaktionen default_account: Standardaccount - create_account: Account erstellen accounts: Accounts tx_sent: Gesendet tx_received: Erhalten @@ -65,18 +73,22 @@ wallets: tx_canceled: Abgebrochen tx_confirmed: Bestätigt txs: Transaktionen + tx: messages: Nachrichten transport: Transport + input_finalize_desc: input_slatepack_desc: 'Geben Sie eine Nachricht ein, um eine Antwort zu erstellen oder die Transaktion abzuschließen:' - send_slatepack_desc: 'Senden Sie eine Nachricht an den Empfänger, um die Transaktion abzuschließen:' parse_slatepack_err: 'Bei der Verarbeitung der Nachricht ist ein Fehler aufgetreten. Überprüfen Sie die Eingabedaten:' pay_balance_error: 'Der Kontostand reicht nicht aus, um %{amount} ツ und die Netzwerkgebühr zu bezahlen.' parse_i1_slatepack_desc: 'Um %{amount} zu zahlen, senden Sie diese Nachricht an den Empfänger:' parse_i2_slatepack_desc: 'Schließen Sie die Transaktion ab, um %{amount} ツ zu erhalten' + parse_i3_slatepack_desc: parse_s1_slatepack_desc: 'Um %{amount} zu erhalten, senden Sie diese Nachricht an den Absender:' parse_s2_slatepack_desc: 'Schließen Sie die Transaktion ab, um %{amount} ツ zu senden' - response_slatepack_err: 'Beim Erstellen der Antwort ist ein Fehler aufgetreten. Überprüfen Sie die Eingabedaten:' - response_exists_err: 'Eine solche Transaktion existiert bereits:' + parse_s3_slatepack_desc: + resp_slatepack_err: 'Beim Erstellen der Antwort ist ein Fehler aufgetreten. Überprüfen Sie die Eingabedaten:' + resp_exists_err: 'Eine solche Transaktion existiert bereits:' + resp_canceled_err: create_request_desc: 'Erstellen Sie eine Anfrage zum Senden oder Empfangen der Gelder:' send_request_desc: 'Sie haben eine Anfrage zum Senden von %{amount} ツ erstellt. Senden Sie diese Nachricht an den Empfänger:' send_slatepack_err: Beim Erstellen der Anfrage zum Senden von Geldern ist ein Fehler aufgetreten. Überprüfen Sie die Eingabedaten. @@ -100,9 +112,25 @@ wallets: receive: Empfangen settings: Wallet Einstellungen change_server_confirmation: Um die Änderung der Verbindungseinstellungen zu übernehmen, müssen Sie Ihr Wallet erneut öffnen. Jetzt wieder öffnen? +transport: + desc: + tor_network: + connected: + connecting: + disconnecting: + conn_error: + disconnected: + receiver_address: + incorrect_addr_err: + tor_send_error: + tor_autorun_desc: + tor_sending: + tor_settings: network: self: Netzwerk type: 'Netzwerk Typ:' + mainnet: + testnet: connections: Verbindungen node: Integrierte Node metrics: Metriken @@ -115,6 +143,7 @@ network: available: Verfügbar not_available: Nicht verfügbar availability_check: Verfügbarkeitsprüfung + android_warning: sync_status: node_restarting: Node wird neu gestartet node_down: Node ist ausgefallen @@ -236,4 +265,4 @@ modal: add: Hinzufügen modal_exit: description: Sind Sie sicher, dass Sie die Anwendung beenden wollen? - exit: Schließen + exit: Schließen \ No newline at end of file -- 2.39.5 From e81a826ccba75500086c23327072a89d7d3203a0 Mon Sep 17 00:00:00 2001 From: Max <63856008+Maxnflaxl@users.noreply.github.com> Date: Fri, 28 Jun 2024 16:57:14 +0200 Subject: [PATCH 45/46] Update de.yml add new translations --- locales/de.yml | 98 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 38 deletions(-) diff --git a/locales/de.yml b/locales/de.yml index 143a4ed..9bcabde 100644 --- a/locales/de.yml +++ b/locales/de.yml @@ -1,4 +1,4 @@ -lang_name: +lang_name: Deutsch copy: Kopieren paste: Einfügen continue: Weiter @@ -11,13 +11,21 @@ show: Zeigen delete: Löschen clear: Clear create: Erstellen -id: -kernel: -settings: -language: -scan: -qr_code: -repeat: +id: ID +kernel: Kernel +settings: Einstellungen +language: Sprache +scan: Scannen +qr_code: QR-Code +scan_qr: QR-Code scannen +repeat: wiederholen +scan_result: Scan Ergebnis +back: zurück +share: teilen +theme: 'Theme:' +dark: dunkel +light: hell +choose_file: Datei auswählen wallets: await_conf_amount: Erwarte Bestätigung await_fin_amount: Warten auf die Fertigstellung @@ -57,7 +65,7 @@ wallets: closing: Schließen checking: Überprüfung default_wallet: Standard-Wallet - new_account_desc: + new_account_desc: 'Namen des neuen Accounts eingeben:' wallet_loading: Wallet wird geladen wallet_closing: Wallet schließen wallet_checking: Wallet prüfen @@ -69,26 +77,26 @@ wallets: tx_sending: Senden tx_receiving: Erhalten tx_confirming: Erwarte Bestätigung - tx_cancelling: Abbrechen tx_canceled: Abgebrochen + tx_cancelling: Abbrechen + tx_finalizing: Finalisierung tx_confirmed: Bestätigt txs: Transaktionen - tx: + tx: Transaktion messages: Nachrichten transport: Transport - input_finalize_desc: input_slatepack_desc: 'Geben Sie eine Nachricht ein, um eine Antwort zu erstellen oder die Transaktion abzuschließen:' parse_slatepack_err: 'Bei der Verarbeitung der Nachricht ist ein Fehler aufgetreten. Überprüfen Sie die Eingabedaten:' pay_balance_error: 'Der Kontostand reicht nicht aus, um %{amount} ツ und die Netzwerkgebühr zu bezahlen.' parse_i1_slatepack_desc: 'Um %{amount} zu zahlen, senden Sie diese Nachricht an den Empfänger:' - parse_i2_slatepack_desc: 'Schließen Sie die Transaktion ab, um %{amount} ツ zu erhalten' - parse_i3_slatepack_desc: + parse_i2_slatepack_desc: 'Schließen Sie die Transaktion ab, um %{amount} ツ zu erhalten:' + parse_i3_slatepack_desc: 'Transaktion posten, um den Erhalt von %{amount} abzuschließen ツ:' parse_s1_slatepack_desc: 'Um %{amount} zu erhalten, senden Sie diese Nachricht an den Absender:' - parse_s2_slatepack_desc: 'Schließen Sie die Transaktion ab, um %{amount} ツ zu senden' - parse_s3_slatepack_desc: + parse_s2_slatepack_desc: 'Schließen Sie die Transaktion ab, um %{amount} ツ zu senden:' + parse_s3_slatepack_desc: 'Transaktion posten, um das Senden von %{amount} abzuschließen ツ:' resp_slatepack_err: 'Beim Erstellen der Antwort ist ein Fehler aufgetreten. Überprüfen Sie die Eingabedaten:' - resp_exists_err: 'Eine solche Transaktion existiert bereits:' - resp_canceled_err: + resp_exists_err: 'Eine solche Transaktion existiert bereits.' + resp_canceled_err: 'Eine solche Transaktion wurde schon abgebrochen. create_request_desc: 'Erstellen Sie eine Anfrage zum Senden oder Empfangen der Gelder:' send_request_desc: 'Sie haben eine Anfrage zum Senden von %{amount} ツ erstellt. Senden Sie diese Nachricht an den Empfänger:' send_slatepack_err: Beim Erstellen der Anfrage zum Senden von Geldern ist ein Fehler aufgetreten. Überprüfen Sie die Eingabedaten. @@ -111,26 +119,35 @@ wallets: send: Senden receive: Empfangen settings: Wallet Einstellungen - change_server_confirmation: Um die Änderung der Verbindungseinstellungen zu übernehmen, müssen Sie Ihr Wallet erneut öffnen. Jetzt wieder öffnen? + tx_send_cancel_conf: 'Sind Sie sicher, dass Sie das Senden von %{amount} ツ abbrechen wollen?' + tx_receive_cancel_conf: 'Sind Sie sicher, dass Sie das Empfangen von %{amount} ツ abbrechen wollen?' + rec_phrase_not_found: Wiederhestellungsphrase nicht gefunden. + restore_wallet_desc: Stellen Sie das Wallet wieder her, indem Sie alle Dateien löschen. Wenn die normale Reparatur nicht geholfen hat, müssen Sie Ihr Wallet erneut öffnen. transport: - desc: - tor_network: - connected: - connecting: - disconnecting: - conn_error: - disconnected: - receiver_address: - incorrect_addr_err: - tor_send_error: - tor_autorun_desc: - tor_sending: - tor_settings: + desc: 'Transport verwenden, um Nachrichten synchron zu empfangen oder zu senden:' + tor_network: Tor Netzwek + connected: verbunden + connecting: verbinden + disconnecting: Verbindung trennen + conn_error: Verbindungsproblem + disconnected: Verbindung getrennt + receiver_address: 'Empfängeraddresse:' + incorrect_addr_err: 'Eingegebene Addresse ist inkorrekt:' + tor_send_error: Beim Senden über Tor ist ein Fehler aufgetreten. Stellen Sie sicher, dass der Empfänger online ist. Die Transaktion wurde abgebrochen. + tor_autorun_desc: Gibt an, ob beim Öffnen des Wallets der Tor-Dienst gestartet werden soll, um Transaktionen synchron zu empfangen. + tor_sending: 'Sende %{amount} ツ über Tor' + tor_settings: Tor Einstellungen + bridges: Brücken + bridges_desc: Richten Sie Brücken ein, um die Zensur des Tor-Netzwerks zu umgehen, wenn die normale Verbindung nicht funktioniert. + bin_file: 'Binärdatei:' + conn_line: 'Verbindungsleitung:' + bridges_disabled: Brücken deaktiviert + bridge_name: 'Brücke %{b} network: self: Netzwerk type: 'Netzwerk Typ:' - mainnet: - testnet: + mainnet: Haupt + testnet: Test connections: Verbindungen node: Integrierte Node metrics: Metriken @@ -143,7 +160,7 @@ network: available: Verfügbar not_available: Nicht verfügbar availability_check: Verfügbarkeitsprüfung - android_warning: + android_warning: Achtung an Android-Benutzer. Um integrierte Nodes erfolgreich zu synchronisieren, müssen Sie in den Systemeinstellungen Ihres Telefons den Zugriff auf Benachrichtigungen zulassen und die Beschränkungen für die Akkunutzung für die Grim-Anwendung entfernen. Dies ist ein notwendiger Vorgang, damit die Anwendung im Hintergrund korrekt funktioniert. sync_status: node_restarting: Node wird neu gestartet node_down: Node ist ausgefallen @@ -177,6 +194,11 @@ network_node: data: Daten size: Größe (GB) peers: Peers + error_clean: + resync: Neu synchronisieren + error_p2p_api: 'Während der Initialisierung des %{p2p_api}-Servers ist ein Fehler aufgetreten. Überprüfen Sie die %{p2p_api}-Einstellungen, indem Sie unten auf dem Bildschirm %{settings} auswählen.' + error_config: 'Während der Initialisierung der Konfiguration ist ein Fehler aufgetreten. Überprüfen Sie die Einstellungen, indem Sie unten auf dem Bildschirm %{settings} auswählen.' + error_unknown: 'Während der Initialisierung ist ein Fehler aufgetreten. Überprüfen Sie die integrierten Knoteneinstellungen, indem Sie unten auf dem Bildschirm %{settings} auswählen oder erneut synchronisieren.' network_metrics: loading: Metriken werden nach der Synchronisierung verfügbar sein emission: Emission @@ -256,8 +278,8 @@ network_settings: ban_window_desc: Die Entscheidung über das Verbot trifft der Knoten auf der Grundlage der Korrektheit der von der Gegenstelle erhaltenen Daten. max_inbound_count: 'Maximale Anzahl der eingehenden Peer-Verbindungen:' max_outbound_count: 'Maximale Anzahl von ausgehenden Peer-Verbindungen:' - min_outbound_count: 'Mindestanzahl von ausgehenden Peer-Verbindungen:' - min_outbound_desc: Der Node-Server wird aktiv versuchen, weitere Peers hinzuzufügen, bis er mindestens diese Anzahl erreicht hat. + reset_peers_desc: + reset_peers: modal: cancel: Abbrechen save: Speichern @@ -265,4 +287,4 @@ modal: add: Hinzufügen modal_exit: description: Sind Sie sicher, dass Sie die Anwendung beenden wollen? - exit: Schließen \ No newline at end of file + exit: Schließen -- 2.39.5 From b4f8bbb2c908aa2d62ad037a8f55fe27d1009db4 Mon Sep 17 00:00:00 2001 From: Max <63856008+Maxnflaxl@users.noreply.github.com> Date: Fri, 28 Jun 2024 17:11:18 +0200 Subject: [PATCH 46/46] Update de.yml add missed translations --- locales/de.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/de.yml b/locales/de.yml index 9bcabde..99b54e9 100644 --- a/locales/de.yml +++ b/locales/de.yml @@ -278,8 +278,8 @@ network_settings: ban_window_desc: Die Entscheidung über das Verbot trifft der Knoten auf der Grundlage der Korrektheit der von der Gegenstelle erhaltenen Daten. max_inbound_count: 'Maximale Anzahl der eingehenden Peer-Verbindungen:' max_outbound_count: 'Maximale Anzahl von ausgehenden Peer-Verbindungen:' - reset_peers_desc: - reset_peers: + reset_peers_desc: Peer-Daten zurücksetzen. Verwenden Sie diese Funktion nur, wenn es Probleme beim finden von Peers gibt. + reset_peers: Peers zurücksetzten modal: cancel: Abbrechen save: Speichern -- 2.39.5