From e0244a51dc3f01d49db7e2930f66d8f48d7d587f Mon Sep 17 00:00:00 2001 From: woodser Date: Sat, 26 Oct 2024 00:20:27 -0400 Subject: [PATCH 01/46] update readme: add run section, remove quality grade, fix bounty links --- README.md | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 7f4d1eb2..bc883554 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,8 @@
Haveno logo - [![Codacy Badge](https://app.codacy.com/project/badge/Grade/505405b43cb74d5a996f106a3371588e)](https://app.codacy.com/gh/haveno-dex/haveno/dashboard) ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/haveno-dex/haveno/build.yml?branch=master) - [![GitHub issues with bounty](https://img.shields.io/github/issues-search/haveno-dex/haveno?color=%23fef2c0&label=Issues%20with%20bounties&query=project%3Ahaveno-dex%2F2)](https://github.com/orgs/haveno-dex/projects/2) | + [![GitHub issues with bounty](https://img.shields.io/github/issues-search/haveno-dex/haveno?color=%23fef2c0&label=Issues%20with%20bounties&query=is%3Aopen+is%3Aissue+label%3A%F0%9F%92%B0bounty)](https://github.com/haveno-dex/haveno/issues?q=is%3Aopen+is%3Aissue+label%3A%F0%9F%92%B0bounty) [![Twitter Follow](https://img.shields.io/twitter/follow/HavenoDEX?style=social)](https://twitter.com/havenodex) [![Matrix rooms](https://img.shields.io/badge/Matrix%20room-%23haveno-blue)](https://matrix.to/#/#haveno:monero.social) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](https://github.com/haveno-dex/.github/blob/master/CODE_OF_CONDUCT.md)
@@ -14,7 +13,7 @@ Haveno (pronounced ha‧ve‧no) is an open source platform to exchange [Monero] Main features: -- All communications are routed through **Tor**, to preserve your privacy +- Communications are routed through **Tor**, to preserve your privacy. - Trades are **peer-to-peer**: trades on Haveno happen between people only, there is no central authority. @@ -24,24 +23,22 @@ Main features: See the [FAQ on our website](https://haveno.exchange/faq/) for more information. -## Status of the project +## Running Haveno -Haveno can be used on Monero's main network by using a third party Haveno network. We do not officially endorse any networks at this time. +Haveno can be used on Monero's main network by using a third party installer and network. We do not endorse any networks at this time. A test network is also available for users to make test trades using Monero's stagenet. See the [instructions](https://github.com/haveno-dex/haveno/blob/master/docs/installing.md) to build Haveno and connect to the network. Note that Haveno is being actively developed. If you find issues or bugs, please let us know. -Main repositories: +## Main repositories - **[haveno](https://github.com/haveno-dex/haveno)** - This repository. The core of Haveno. -- **[haveno-ui](https://github.com/haveno-dex/haveno-ui)** - The user interface. - **[haveno-ts](https://github.com/haveno-dex/haveno-ts)** - TypeScript library for using Haveno. +- **[haveno-ui](https://github.com/haveno-dex/haveno-ui)** - A new user interface (WIP). - **[haveno-meta](https://github.com/haveno-dex/haveno-meta)** - For project-wide discussions and proposals. -If you wish to help, take a look at the repositories above and look for open issues. We run a bounty program to incentivize development. See [Bounties](#bounties) - -The PGP keys of the core team members are in `gpg_keys/`. +If you wish to help, take a look at the repositories above and look for open issues. We run a bounty program to incentivize development. See [Bounties](#bounties). ## Keep in touch and help out! @@ -63,7 +60,7 @@ If you are not able to contribute code and want to contribute development resour ## Bounties -To incentivize development and reward contributors we adopt a simple bounty system. Contributors may be awarded bounties after completing a task (resolving an issue). Take a look at the issues eligible for a bounty on the [dedicated Kanban board](https://github.com/orgs/haveno-dex/projects/2) or look for [issues labelled '💰bounty'](https://github.com/haveno-dex/haveno/issues?q=is%3Aissue+is%3Aopen+label%3A%F0%9F%92%B0bounty) in the main `haveno` repository. [Details and conditions for receiving a bounty](docs/bounties.md). +To incentivize development and reward contributors, we adopt a simple bounty system. Contributors may be awarded bounties after completing a task (resolving an issue). Take a look at the [issues labeled '💰bounty'](https://github.com/haveno-dex/haveno/issues?q=is%3Aopen+is%3Aissue+label%3A%F0%9F%92%B0bounty) in the main `haveno` repository. [Details and conditions for receiving a bounty](docs/bounties.md). ## Support and sponsorships From 3520694251961900ee1335edf1791564a7391f23 Mon Sep 17 00:00:00 2001 From: woodser Date: Thu, 24 Oct 2024 12:15:06 -0400 Subject: [PATCH 02/46] update tails instructions from gr8ful4 --- scripts/install_tails/README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/scripts/install_tails/README.md b/scripts/install_tails/README.md index b7182559..184f31a1 100644 --- a/scripts/install_tails/README.md +++ b/scripts/install_tails/README.md @@ -1,21 +1,22 @@ # Install Haveno on Tails -Install Haveno on Tails by following these steps: +After you already have a [Tails USB](https://tails.net/install/linux/index.en.html#download): + +1. Enable [persistent storage](https://tails.net/doc/persistent_storage/index.en.html). +2. Set up [administration password](https://tails.net/doc/first_steps/welcome_screen/administration_password/). +3. Activate dotfiles in persistent storage settings. +4. Execute the following command in the terminal to download and execute the installation script. Enter the administration password when requested. -1. Enable persistent storage dotfiles and admin password before starting Tails. -2. Execute a one-line installation command to download and install Haveno: - ``` - curl -x socks5h://127.0.0.1:9050 -fsSLO https://github.com/haveno-dex/haveno/raw/master/scripts/install_tails/haveno-install.sh && bash haveno-install.sh "" "" + curl -x socks5h://127.0.0.1:9050 -fsSLO https://github.com/haveno-dex/haveno/raw/master/scripts/install_tails/haveno-install.sh && bash haveno-install.sh ``` Replace the binary zip URL and PGP fingerprint for the network you're using. For example: ``` - curl -x socks5h://127.0.0.1:9050 -fsSLO https://github.com/haveno-dex/haveno/raw/master/scripts/install_tails/haveno-install.sh && bash haveno-install.sh "https://github.com/havenoexample/haveno-example/releases/download/v1.0.12/haveno-linux-deb.zip" "FAA2 4D87 8B8D 36C9 0120 A897 CA02 DAC1 2DAE 2D0F" + curl -x socks5h://127.0.0.1:9050 -fsSLO https://github.com/haveno-dex/haveno/raw/master/scripts/install_tails/haveno-install.sh && bash haveno-install.sh https://github.com/havenoexample/haveno-example/releases/download/v1.0.12/haveno-linux-deb.zip FAA24D878B8D36C90120A897CA02DAC12DAE2D0F ``` - -3. Upon successful execution of the script (no errors), the Haveno release will be installed to persistent storage and can be launched via the desktop shortcut in the 'Other' section of the start menu. +4. Start Haveno by finding the icon in the launcher under **Applications > Other**. > [!note] > If you have already installed Haveno on Tails, we recommend moving your data directory (/home/amnesia/Persistent/Haveno-example) to the new default location (/home/amnesia/Persistent/haveno/Data/Haveno-example), to retain your history and for future support. \ No newline at end of file From e0cdef8844ed0e39f67244ae077a0c98f66c8604 Mon Sep 17 00:00:00 2001 From: woodser Date: Sat, 26 Oct 2024 14:09:53 -0400 Subject: [PATCH 03/46] update installing section in readme --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bc883554..e8d40e8e 100644 --- a/README.md +++ b/README.md @@ -23,11 +23,13 @@ Main features: See the [FAQ on our website](https://haveno.exchange/faq/) for more information. -## Running Haveno +## Installing Haveno -Haveno can be used on Monero's main network by using a third party installer and network. We do not endorse any networks at this time. +Haveno can be installed on Linux, macOS, and Windows by using a third party installer and network. We do not endorse any networks at this time. -A test network is also available for users to make test trades using Monero's stagenet. See the [instructions](https://github.com/haveno-dex/haveno/blob/master/docs/installing.md) to build Haveno and connect to the network. +A test network is also available for users to make test trades using Monero's stagenet. See the [instructions](https://github.com/haveno-dex/haveno/blob/master/docs/installing.md) to build Haveno and connect to the test network. + +Alternatively, you can [start your own network](https://github.com/haveno-dex/haveno/blob/master/docs/create-mainnet.md). Note that Haveno is being actively developed. If you find issues or bugs, please let us know. From 4821a8d284fc09b73ed1d6cf47a377af0e103b9a Mon Sep 17 00:00:00 2001 From: XMRZombie Date: Wed, 30 Oct 2024 13:26:08 +0000 Subject: [PATCH 04/46] test taproot address for Bitcoin (#1362) --- assets/src/test/java/haveno/asset/coins/BitcoinTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/src/test/java/haveno/asset/coins/BitcoinTest.java b/assets/src/test/java/haveno/asset/coins/BitcoinTest.java index ad3bcee2..e90c25b6 100644 --- a/assets/src/test/java/haveno/asset/coins/BitcoinTest.java +++ b/assets/src/test/java/haveno/asset/coins/BitcoinTest.java @@ -32,6 +32,7 @@ public class BitcoinTest extends AbstractAssetTest { assertValidAddress("3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX"); assertValidAddress("1111111111111111111114oLvT2"); assertValidAddress("1BitcoinEaterAddressDontSendf59kuE"); + assertValidAddress("bc1qj89046x7zv6pm4n00qgqp505nvljnfp6xfznyw"); } @Test From 6675390e20af34773c64339aad53ddfeb3ab4da4 Mon Sep 17 00:00:00 2001 From: woodser Date: Wed, 30 Oct 2024 15:15:59 -0400 Subject: [PATCH 05/46] update price node address --- .../src/main/java/haveno/core/provider/ProvidersRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/haveno/core/provider/ProvidersRepository.java b/core/src/main/java/haveno/core/provider/ProvidersRepository.java index f8c7da06..2a657bc8 100644 --- a/core/src/main/java/haveno/core/provider/ProvidersRepository.java +++ b/core/src/main/java/haveno/core/provider/ProvidersRepository.java @@ -49,7 +49,7 @@ import lombok.extern.slf4j.Slf4j; public class ProvidersRepository { private static final List DEFAULT_NODES = Arrays.asList( "http://elaxlgigphpicy5q7pi5wkz2ko2vgjbq4576vic7febmx4xcxvk6deqd.onion/", // Haveno - "http://a66ulzwhhudtqy6k2efnhodj2n6wnc5mnzjs3ocqtf47lwtcuo4wxyqd.onion/" // Cake + "http://lrrgpezvdrbpoqvkavzobmj7dr2otxc5x6wgktrw337bk6mxsvfp5yid.onion/" // Cake ); private final Config config; From 6b91b057e55a32953f55f0c760b56f5bf5677506 Mon Sep 17 00:00:00 2001 From: bvcxza <175357591+bvcxza@users.noreply.github.com> Date: Thu, 31 Oct 2024 11:01:00 -0300 Subject: [PATCH 06/46] fix background color of txid when funds are withdrawn in dark mode (#1367) --- desktop/src/main/java/haveno/desktop/theme-dark.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/desktop/src/main/java/haveno/desktop/theme-dark.css b/desktop/src/main/java/haveno/desktop/theme-dark.css index db5c66a2..8e7345b3 100644 --- a/desktop/src/main/java/haveno/desktop/theme-dark.css +++ b/desktop/src/main/java/haveno/desktop/theme-dark.css @@ -241,6 +241,10 @@ -fx-border-width: 0 0 10 0; } +#address-text-field.jfx-text-field:readonly { + -fx-background-color: derive(-bs-background-color, 15%); +} + .wallet-seed-words { -fx-text-fill: -bs-color-gray-6; } From c855d66a0c13f71fb3894a60c119b09d12bca9fa Mon Sep 17 00:00:00 2001 From: niyid Date: Thu, 31 Oct 2024 20:06:14 +0100 Subject: [PATCH 07/46] fix sorting on buy > amount column ( --- .../haveno/desktop/main/offer/offerbook/OfferBookView.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java index 3c0f1705..0a37b36e 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java @@ -366,7 +366,6 @@ abstract public class OfferBookView model.onShowOffersMatchingMyAccounts(matchingOffersToggle.isSelected())); - volumeColumn.sortableProperty().bind(model.showAllTradeCurrenciesProperty.not()); model.getOfferList().comparatorProperty().bind(tableView.comparatorProperty()); amountColumn.sortTypeProperty().addListener((observable, oldValue, newValue) -> { @@ -775,6 +774,7 @@ abstract public class OfferBookView getAmountColumn() { AutoTooltipTableColumn column = new AutoTooltipTableColumn<>(Res.get("shared.XMRMinMax"), Res.get("shared.amountHelp")); column.setMinWidth(100); + column.setSortable(true); column.getStyleClass().add("number-column"); column.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); column.setCellFactory( @@ -918,6 +918,7 @@ abstract public class OfferBookView column = new AutoTooltipTableColumn<>("") { { setMinWidth(125); + setSortable(true); } }; column.getStyleClass().add("number-column"); From 485746381c3d19421271a474ad8579d26c87b652 Mon Sep 17 00:00:00 2001 From: niyid Date: Fri, 1 Nov 2024 11:14:30 +0100 Subject: [PATCH 08/46] Memo sorting fix (#1354) --- .../desktop/main/funds/transactions/TransactionsView.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.java b/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.java index 284d3369..9544f507 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.java @@ -162,8 +162,7 @@ public class TransactionsView extends ActivatableView { transactionColumn.setComparator(Comparator.comparing(TransactionsListItem::getTxId)); amountColumn.setComparator(Comparator.comparing(TransactionsListItem::getAmount)); confidenceColumn.setComparator(Comparator.comparingLong(TransactionsListItem::getNumConfirmations)); - memoColumn.setComparator(Comparator.comparing(TransactionsListItem::getMemo)); - + memoColumn.setComparator(Comparator.comparing(TransactionsListItem::getMemo, Comparator.nullsLast(Comparator.naturalOrder()))); dateColumn.setSortType(TableColumn.SortType.DESCENDING); tableView.getSortOrder().add(dateColumn); From aee0c1c0b2773dda87789a797df2957ac1c3e062 Mon Sep 17 00:00:00 2001 From: woodser Date: Tue, 5 Nov 2024 10:34:42 -0500 Subject: [PATCH 09/46] validate form fields on create payment account from form --- .../haveno/core/api/CorePaymentAccountsService.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/src/main/java/haveno/core/api/CorePaymentAccountsService.java b/core/src/main/java/haveno/core/api/CorePaymentAccountsService.java index dcf1ef0e..b506a00a 100644 --- a/core/src/main/java/haveno/core/api/CorePaymentAccountsService.java +++ b/core/src/main/java/haveno/core/api/CorePaymentAccountsService.java @@ -64,9 +64,14 @@ class CorePaymentAccountsService { } PaymentAccount createPaymentAccount(PaymentAccountForm form) { + validateFormFields(form); PaymentAccount paymentAccount = form.toPaymentAccount(); setSelectedTradeCurrency(paymentAccount); // TODO: selected trade currency is function of offer, not payment account payload verifyPaymentAccountHasRequiredFields(paymentAccount); + if (paymentAccount instanceof CryptoCurrencyAccount) { + CryptoCurrencyAccount cryptoAccount = (CryptoCurrencyAccount) paymentAccount; + verifyCryptoCurrencyAddress(cryptoAccount.getSingleTradeCurrency().getCode(), cryptoAccount.getAddress()); + } user.addPaymentAccountIfNotExists(paymentAccount); accountAgeWitnessService.publishMyAccountAgeWitness(paymentAccount.getPaymentAccountPayload()); log.info("Saved payment account with id {} and payment method {}.", @@ -166,6 +171,12 @@ class CorePaymentAccountsService { .collect(Collectors.toList()); } + private void validateFormFields(PaymentAccountForm form) { + for (PaymentAccountFormField field : form.getFields()) { + validateFormField(form, field.getId(), field.getValue()); + } + } + void validateFormField(PaymentAccountForm form, PaymentAccountFormField.FieldId fieldId, String value) { // get payment method id From ee889c52388314023fdd080ebf6b03b719fb5615 Mon Sep 17 00:00:00 2001 From: nahuhh <50635951+nahuhh@users.noreply.github.com> Date: Thu, 7 Nov 2024 10:54:52 +0000 Subject: [PATCH 10/46] scripts: use wget for tails setup (#1381) --- scripts/install_tails/haveno-install.sh | 34 +++++++++++-------------- 1 file changed, 15 insertions(+), 19 deletions(-) mode change 100644 => 100755 scripts/install_tails/haveno-install.sh diff --git a/scripts/install_tails/haveno-install.sh b/scripts/install_tails/haveno-install.sh old mode 100644 new mode 100755 index 3f332cf2..e9a8c37b --- a/scripts/install_tails/haveno-install.sh +++ b/scripts/install_tails/haveno-install.sh @@ -38,29 +38,21 @@ install_dir="${persistence_dir}/haveno/Install" dotfiles_dir="/live/persistence/TailsData_unlocked/dotfiles" persistent_desktop_dir="$dotfiles_dir/.local/share/applications" local_desktop_dir="/home/amnesia/.local/share/applications" - - -# Install dependencies -echo_blue "Installing dependencies ..." -sudo apt update && sudo apt install -y curl unzip - - -# Remove stale resources -rm -rf "${assets_dir}" +wget_flags="--tries=10 --timeout=10 --waitretry=5 --retry-connrefused --show-progress" # Create temp location for downloads echo_blue "Creating temporary directory for Haveno resources ..." -mkdir "${assets_dir}" || { echo_red "Failed to create directory ${assets_dir}"; exit 1; } +mkdir -p "${assets_dir}" || { echo_red "Failed to create directory ${assets_dir}"; exit 1; } # Download resources echo_blue "Downloading resources for Haveno on Tails ..." -curl --retry 10 --retry-delay 5 -fsSLo /tmp/assets/exec.sh https://github.com/haveno-dex/haveno/raw/master/scripts/install_tails/assets/exec.sh || { echo_red "Failed to download resource (exec.sh)."; exit 1; } -curl --retry 10 --retry-delay 5 -fsSLo /tmp/assets/install.sh https://github.com/haveno-dex/haveno/raw/master/scripts/install_tails/assets/install.sh || { echo_red "Failed to download resource (install.sh)."; exit 1; } -curl --retry 10 --retry-delay 5 -fsSLo /tmp/assets/haveno.desktop https://github.com/haveno-dex/haveno/raw/master/scripts/install_tails/assets/haveno.desktop || { echo_red "Failed to resource (haveno.desktop)."; exit 1; } -curl --retry 10 --retry-delay 5 -fsSLo /tmp/assets/icon.png https://raw.githubusercontent.com/haveno-dex/haveno/master/scripts/install_tails/assets/icon.png || { echo_red "Failed to download resource (icon.png)."; exit 1; } -curl --retry 10 --retry-delay 5 -fsSLo /tmp/assets/haveno.yml https://github.com/haveno-dex/haveno/raw/master/scripts/install_tails/assets/haveno.yml || { echo_red "Failed to download resource (haveno.yml)."; exit 1; } +wget "${wget_flags}" -cqP "${assets_dir}" https://github.com/haveno-dex/haveno/raw/master/scripts/install_tails/assets/exec.sh || { echo_red "Failed to download resource (exec.sh)."; exit 1; } +wget "${wget_flags}" -cqP "${assets_dir}" https://github.com/haveno-dex/haveno/raw/master/scripts/install_tails/assets/install.sh || { echo_red "Failed to download resource (install.sh)."; exit 1; } +wget "${wget_flags}" -cqP "${assets_dir}" https://github.com/haveno-dex/haveno/raw/master/scripts/install_tails/assets/haveno.desktop || { echo_red "Failed to resource (haveno.desktop)."; exit 1; } +wget "${wget_flags}" -cqP "${assets_dir}" https://raw.githubusercontent.com/haveno-dex/haveno/master/scripts/install_tails/assets/icon.png || { echo_red "Failed to download resource (icon.png)."; exit 1; } +wget "${wget_flags}" -cqP "${assets_dir}" https://github.com/haveno-dex/haveno/raw/master/scripts/install_tails/assets/haveno.yml || { echo_red "Failed to download resource (haveno.yml)."; exit 1; } # Create persistent directory @@ -92,17 +84,17 @@ fi # Download Haveno binary echo_blue "Downloading Haveno from URL provided ..." -curl --retry 10 --retry-delay 5 -L -o "${binary_filename}" "${user_url}" || { echo_red "Failed to download Haveno binary."; exit 1; } +wget "${wget_flags}" -cq "${user_url}" || { echo_red "Failed to download Haveno binary."; exit 1; } # Download Haveno signature file echo_blue "Downloading Haveno signature ..." -curl --retry 10 --retry-delay 5 -L -o "${signature_filename}" "${base_url}""${signature_filename}" || { echo_red "Failed to download Haveno signature."; exit 1; } +wget "${wget_flags}" -cq "${base_url}""${signature_filename}" || { echo_red "Failed to download Haveno signature."; exit 1; } # Download the GPG key echo_blue "Downloading signing GPG key ..." -curl --retry 10 --retry-delay 5 -L -o "${key_filename}" "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x$(echo "$expected_fingerprint" | tr -d ' ')" || { echo_red "Failed to download GPG key."; exit 1; } +wget "${wget_flags}" -cqO "${key_filename}" "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x$(echo "$expected_fingerprint" | tr -d ' ')" || { echo_red "Failed to download GPG key."; exit 1; } # Import the GPG key @@ -132,7 +124,7 @@ OUTPUT=$(gpg --digest-algo SHA256 --verify "${signature_filename}" "${binary_fil if ! echo "$OUTPUT" | grep -q "Good signature from"; then echo_red "Verification failed: $OUTPUT" exit 1; - else unzip "${binary_filename}" && mv haveno*.deb "${package_filename}" + else 7z x "${binary_filename}" && mv haveno*.deb "${package_filename}" fi echo_blue "Haveno binaries have been successfully verified." @@ -148,5 +140,9 @@ mv "${binary_filename}" "${package_filename}" "${key_filename}" "${signature_fil echo_blue "Files moved to persistent directory ${install_dir}" +# Remove stale resources +rm -rf "${assets_dir}" + + # Completed confirmation echo_blue "Haveno installation setup completed successfully." From b0fc86431343a91392608fbd573a34f96ff3b4d1 Mon Sep 17 00:00:00 2001 From: woodser Date: Thu, 7 Nov 2024 05:56:24 -0500 Subject: [PATCH 11/46] remove '-x socks5h' and update url for tails install (#1385) Co-authored-by: nahuhh <50635951+nahuhh@users.noreply.github.com> --- scripts/install_tails/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/install_tails/README.md b/scripts/install_tails/README.md index 184f31a1..05b6470f 100644 --- a/scripts/install_tails/README.md +++ b/scripts/install_tails/README.md @@ -5,18 +5,18 @@ After you already have a [Tails USB](https://tails.net/install/linux/index.en.ht 1. Enable [persistent storage](https://tails.net/doc/persistent_storage/index.en.html). 2. Set up [administration password](https://tails.net/doc/first_steps/welcome_screen/administration_password/). 3. Activate dotfiles in persistent storage settings. -4. Execute the following command in the terminal to download and execute the installation script. Enter the administration password when requested. +4. Execute the following command in the terminal to download and execute the installation script. ``` - curl -x socks5h://127.0.0.1:9050 -fsSLO https://github.com/haveno-dex/haveno/raw/master/scripts/install_tails/haveno-install.sh && bash haveno-install.sh + curl -fsSLO https://github.com/haveno-dex/haveno/raw/master/scripts/install_tails/haveno-install.sh && bash haveno-install.sh ``` Replace the binary zip URL and PGP fingerprint for the network you're using. For example: ``` - curl -x socks5h://127.0.0.1:9050 -fsSLO https://github.com/haveno-dex/haveno/raw/master/scripts/install_tails/haveno-install.sh && bash haveno-install.sh https://github.com/havenoexample/haveno-example/releases/download/v1.0.12/haveno-linux-deb.zip FAA24D878B8D36C90120A897CA02DAC12DAE2D0F + curl -fsSLO https://github.com/haveno-dex/haveno/raw/master/scripts/install_tails/haveno-install.sh && bash haveno-install.sh https://github.com/havenoexample/haveno-example/releases/latest/download/haveno-linux-deb.zip FAA24D878B8D36C90120A897CA02DAC12DAE2D0F ``` -4. Start Haveno by finding the icon in the launcher under **Applications > Other**. +5. Start Haveno by finding the icon in the launcher under **Applications > Other**. > [!note] > If you have already installed Haveno on Tails, we recommend moving your data directory (/home/amnesia/Persistent/Haveno-example) to the new default location (/home/amnesia/Persistent/haveno/Data/Haveno-example), to retain your history and for future support. \ No newline at end of file From a00930aa9e5e9a963f3cfc60b463dd649e4ed842 Mon Sep 17 00:00:00 2001 From: woodser Date: Wed, 23 Oct 2024 12:03:45 -0400 Subject: [PATCH 12/46] focus on password input in password dialog window --- .../main/java/haveno/desktop/app/HavenoAppMain.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java b/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java index 353895e6..a1aee58c 100644 --- a/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java +++ b/desktop/src/main/java/haveno/desktop/app/HavenoAppMain.java @@ -38,6 +38,7 @@ import javafx.scene.image.ImageView; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.stage.Stage; +import javafx.stage.Window; import lombok.extern.slf4j.Slf4j; import java.util.Optional; @@ -228,6 +229,17 @@ public class HavenoAppMain extends HavenoExecutable { return null; } }); + + // Focus the password field when dialog is shown + Window window = getDialogPane().getScene().getWindow(); + if (window instanceof Stage) { + Stage dialogStage = (Stage) window; + dialogStage.focusedProperty().addListener((observable, oldValue, newValue) -> { + if (newValue) { + passwordField.requestFocus(); + } + }); + } } } } From 072401386e2d67db8382d12ed304233659f3b49c Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 14 Oct 2024 09:44:03 -0400 Subject: [PATCH 13/46] improve sound compatibility for linux --- core/src/main/java/haveno/core/trade/HavenoUtils.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/HavenoUtils.java b/core/src/main/java/haveno/core/trade/HavenoUtils.java index 3032492b..06133e16 100644 --- a/core/src/main/java/haveno/core/trade/HavenoUtils.java +++ b/core/src/main/java/haveno/core/trade/HavenoUtils.java @@ -575,14 +575,14 @@ public class HavenoUtils { // get original format AudioFormat baseFormat = audioInputStream.getFormat(); - // set target format: PCM_SIGNED, 16-bit + // set target format: PCM_SIGNED, 16-bit, 44100 Hz AudioFormat targetFormat = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, - baseFormat.getSampleRate(), + 44100.0f, 16, // 16-bit instead of 32-bit float baseFormat.getChannels(), baseFormat.getChannels() * 2, // Frame size: 2 bytes per channel (16-bit) - baseFormat.getSampleRate(), + 44100.0f, false // Little-endian ); From a9c975466ef0599d41017d323f4ac501a1b472b1 Mon Sep 17 00:00:00 2001 From: woodser Date: Tue, 15 Oct 2024 18:21:57 -0400 Subject: [PATCH 14/46] log warning on failure to sign and publish peer account age witness --- .../core/account/witness/AccountAgeWitnessService.java | 9 ++++----- .../protocol/tasks/ProcessPaymentReceivedMessage.java | 5 +---- .../protocol/tasks/SellerSendPaymentReceivedMessage.java | 8 ++++++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/haveno/core/account/witness/AccountAgeWitnessService.java b/core/src/main/java/haveno/core/account/witness/AccountAgeWitnessService.java index deab92eb..4d91d75e 100644 --- a/core/src/main/java/haveno/core/account/witness/AccountAgeWitnessService.java +++ b/core/src/main/java/haveno/core/account/witness/AccountAgeWitnessService.java @@ -737,14 +737,13 @@ public class AccountAgeWitnessService { } public Optional traderSignAndPublishPeersAccountAgeWitness(Trade trade) { - AccountAgeWitness peersWitness = findTradePeerWitness(trade).orElse(null); - BigInteger tradeAmount = trade.getAmount(); checkNotNull(trade.getTradePeer().getPubKeyRing(), "Peer must have a keyring"); PublicKey peersPubKey = trade.getTradePeer().getPubKeyRing().getSignaturePubKey(); - checkNotNull(peersWitness, "Not able to find peers witness, unable to sign for trade {}", - trade.toString()); - checkNotNull(tradeAmount, "Trade amount must not be null"); checkNotNull(peersPubKey, "Peers pub key must not be null"); + AccountAgeWitness peersWitness = findTradePeerWitness(trade).orElse(null); + checkNotNull(peersWitness, "Not able to find peers witness, unable to sign for trade " + trade.toString()); + BigInteger tradeAmount = trade.getAmount(); + checkNotNull(tradeAmount, "Trade amount must not be null"); try { return signedWitnessService.signAndPublishAccountAgeWitness(tradeAmount, peersWitness, peersPubKey); diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java index 28c61d51..121d87d8 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java @@ -105,12 +105,9 @@ public class ProcessPaymentReceivedMessage extends TradeTask { // advance state, arbitrator auto completes when payout published trade.advanceState(Trade.State.SELLER_SENT_PAYMENT_RECEIVED_MSG); - // publish signed witness + // buyer republishes signed witness for resilience SignedWitness signedWitness = message.getBuyerSignedWitness(); if (signedWitness != null && trade instanceof BuyerTrade) { - // We received the signedWitness from the seller and publish the data to the network. - // The signer has published it as well but we prefer to re-do it on our side as well to achieve higher - // resilience. processModel.getAccountAgeWitnessService().publishOwnSignedWitness(signedWitness); } diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java b/core/src/main/java/haveno/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java index fe2f1ac4..cbfa0e32 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java @@ -90,8 +90,12 @@ public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessag // sign account witness AccountAgeWitnessService accountAgeWitnessService = processModel.getAccountAgeWitnessService(); if (accountAgeWitnessService.isSignWitnessTrade(trade)) { - accountAgeWitnessService.traderSignAndPublishPeersAccountAgeWitness(trade).ifPresent(witness -> signedWitness = witness); - log.info("{} {} signed and published peers account age witness", trade.getClass().getSimpleName(), trade.getId()); + try { + accountAgeWitnessService.traderSignAndPublishPeersAccountAgeWitness(trade).ifPresent(witness -> signedWitness = witness); + log.info("{} {} signed and published peers account age witness", trade.getClass().getSimpleName(), trade.getId()); + } catch (Exception e) { + log.warn("Failed to sign and publish peer's account age witness for {} {}, error={}\n", getClass().getSimpleName(), trade.getId(), e.getMessage(), e); + } } // We do not use a real unique ID here as we want to be able to re-send the exact same message in case the From e3420de0d8a47c27f77e1ea84557fa8f7b705ba7 Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 8 Nov 2024 08:57:04 -0500 Subject: [PATCH 15/46] increase contrast of main tab labels in light mode --- desktop/src/main/java/haveno/desktop/theme-light.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/src/main/java/haveno/desktop/theme-light.css b/desktop/src/main/java/haveno/desktop/theme-light.css index 8f057a50..b4ab888f 100644 --- a/desktop/src/main/java/haveno/desktop/theme-light.css +++ b/desktop/src/main/java/haveno/desktop/theme-light.css @@ -41,7 +41,7 @@ -bs-rd-green: #0b65da; -bs-rd-green-dark: #3EA34A; -bs-rd-nav-selected: #0b65da; - -bs-rd-nav-deselected: rgba(255, 255, 255, 0.59); + -bs-rd-nav-deselected: rgba(255, 255, 255, 0.75); -bs-rd-nav-background: #0c59bd; -bs-rd-nav-primary-background: #0b65da; -bs-rd-nav-primary-border: #0B65DA; From 22f32f43a087729b51c1b79f4ad5b6948f5f7800 Mon Sep 17 00:00:00 2001 From: woodser Date: Wed, 6 Nov 2024 08:13:53 -0500 Subject: [PATCH 16/46] fix mouse pointer on tab hover in light mode --- desktop/src/main/java/haveno/desktop/haveno.css | 1 + 1 file changed, 1 insertion(+) diff --git a/desktop/src/main/java/haveno/desktop/haveno.css b/desktop/src/main/java/haveno/desktop/haveno.css index 125b4c7b..95b57a3a 100644 --- a/desktop/src/main/java/haveno/desktop/haveno.css +++ b/desktop/src/main/java/haveno/desktop/haveno.css @@ -1267,6 +1267,7 @@ textfield */ -fx-padding: 14; -fx-font-size: 0.769em; -fx-font-weight: normal; + -fx-cursor: hand; } .jfx-tab-pane .depth-container { From ed567beeb394c4396cd485313838260ab7a8f959 Mon Sep 17 00:00:00 2001 From: woodser Date: Wed, 30 Oct 2024 09:18:39 -0400 Subject: [PATCH 17/46] add logging for dispute's trade being null #1364 --- core/src/main/java/haveno/core/trade/TradeManager.java | 1 + .../core/trade/protocol/tasks/ProcessDepositResponse.java | 1 + .../java/haveno/desktop/main/support/dispute/DisputeView.java | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/haveno/core/trade/TradeManager.java b/core/src/main/java/haveno/core/trade/TradeManager.java index 55acf63d..10db542e 100644 --- a/core/src/main/java/haveno/core/trade/TradeManager.java +++ b/core/src/main/java/haveno/core/trade/TradeManager.java @@ -958,6 +958,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi } public void unregisterTrade(Trade trade) { + log.warn("Unregistering {} {}", trade.getClass().getSimpleName(), trade.getId()); removeTrade(trade, true); removeFailedTrade(trade); requestPersistence(); diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessDepositResponse.java b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessDepositResponse.java index e37c6121..dcaf1a7e 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessDepositResponse.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessDepositResponse.java @@ -41,6 +41,7 @@ public class ProcessDepositResponse extends TradeTask { // throw if error DepositResponse message = (DepositResponse) processModel.getTradeMessage(); if (message.getErrorMessage() != null) { + log.warn("Unregistering trade {} {} because deposit response has error message={}", trade.getClass().getSimpleName(), trade.getShortId(), message.getErrorMessage()); trade.setStateIfValidTransitionTo(Trade.State.PUBLISH_DEPOSIT_TX_REQUEST_FAILED); processModel.getTradeManager().unregisterTrade(trade); throw new RuntimeException(message.getErrorMessage()); diff --git a/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java b/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java index 9d88ad23..f62761d4 100644 --- a/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java +++ b/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java @@ -1415,7 +1415,7 @@ public abstract class DisputeView extends ActivatableView implements private String getDisputeStateText(Dispute dispute) { Trade trade = tradeManager.getTrade(dispute.getTradeId()); if (trade == null) { - log.warn("Dispute's trade is null for trade {}", dispute.getTradeId()); + log.warn("Dispute's trade is null for trade {}, defaulting to dispute state text 'closed'", dispute.getTradeId()); return Res.get("support.closed"); } if (dispute.isClosed()) return Res.get("support.closed"); From bc1cfe3ba069f8e906c7ae031c4df2530e4ee03c Mon Sep 17 00:00:00 2001 From: woodser Date: Wed, 16 Oct 2024 15:07:22 -0400 Subject: [PATCH 18/46] exclude unsigned offers from republish to fix warning --- core/src/main/java/haveno/core/offer/OpenOfferManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/haveno/core/offer/OpenOfferManager.java b/core/src/main/java/haveno/core/offer/OpenOfferManager.java index ffed498b..a5c3194c 100644 --- a/core/src/main/java/haveno/core/offer/OpenOfferManager.java +++ b/core/src/main/java/haveno/core/offer/OpenOfferManager.java @@ -1855,7 +1855,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe } private boolean preventedFromPublishing(OpenOffer openOffer) { - return openOffer.isDeactivated() || openOffer.isCanceled(); + return openOffer.isDeactivated() || openOffer.isCanceled() || openOffer.getOffer().getOfferPayload().getArbitratorSigner() == null; } private void startPeriodicRepublishOffersTimer() { From 123a2a8487407d3331af4be93e91661b01689c53 Mon Sep 17 00:00:00 2001 From: woodser Date: Sun, 20 Oct 2024 12:49:05 -0400 Subject: [PATCH 19/46] log error on infinity in offer book chart view without popup #1340 --- .../market/offerbook/OfferBookChartView.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartView.java b/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartView.java index ac57c22b..3d2ec6d8 100644 --- a/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartView.java +++ b/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartView.java @@ -207,16 +207,21 @@ public class OfferBookChartView extends ActivatableViewAndModel Date: Tue, 29 Oct 2024 08:33:36 -0400 Subject: [PATCH 20/46] fix FX application thread error in portfolio view #1356 --- .../java/haveno/desktop/main/portfolio/PortfolioView.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/PortfolioView.java b/desktop/src/main/java/haveno/desktop/main/portfolio/PortfolioView.java index db5759a2..3b17109a 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/PortfolioView.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/PortfolioView.java @@ -140,8 +140,10 @@ public class PortfolioView extends ActivatableView { @Override protected void activate() { failedTradesManager.getObservableList().addListener((ListChangeListener) c -> { - if (failedTradesManager.getObservableList().size() > 0 && root.getTabs().size() == 3) - root.getTabs().add(failedTradesTab); + UserThread.execute(() -> { + if (failedTradesManager.getObservableList().size() > 0 && root.getTabs().size() == 3) + root.getTabs().add(failedTradesTab); + }); }); if (failedTradesManager.getObservableList().size() > 0 && root.getTabs().size() == 3) root.getTabs().add(failedTradesTab); From 0c76c48c652ad77718ca3ea62aec4641b44c815a Mon Sep 17 00:00:00 2001 From: woodser Date: Sun, 3 Nov 2024 10:16:21 -0500 Subject: [PATCH 21/46] refactor buy/sell tab functionality #1351 --- .../java/haveno/core/locale/CurrencyUtil.java | 16 +- .../haveno/core/payment/PaymentAccount.java | 4 + .../java/haveno/core/user/Preferences.java | 10 ++ .../haveno/core/user/PreferencesPayload.java | 8 + .../resources/i18n/displayStrings.properties | 5 +- .../i18n/displayStrings_tr.properties | 4 +- .../java/haveno/desktop/main/MainView.java | 4 +- .../offerbook/OfferBookChartViewModel.java | 4 - .../main/offer/MutableOfferDataModel.java | 7 +- .../haveno/desktop/main/offer/OfferView.java | 133 ++++++++-------- .../desktop/main/offer/OfferViewUtil.java | 29 ++-- .../offer/createoffer/CreateOfferView.java | 10 +- ...BookView.fxml => CryptoOfferBookView.fxml} | 2 +- ...BookView.java => CryptoOfferBookView.java} | 34 ++-- .../offerbook/CryptoOfferBookViewModel.java | 148 ++++++++++++++++++ ...erBookView.fxml => FiatOfferBookView.fxml} | 2 +- ...erBookView.java => FiatOfferBookView.java} | 18 +-- ...Model.java => FiatOfferBookViewModel.java} | 37 ++--- .../offer/offerbook/OtherOfferBookView.java | 20 +-- .../offerbook/OtherOfferBookViewModel.java | 69 ++++---- .../TopCryptoOfferBookViewModel.java | 116 -------------- .../java/haveno/desktop/util/GUIUtil.java | 10 -- .../offerbook/OfferBookViewModelTest.java | 22 +-- proto/src/main/proto/pb.proto | 2 + 24 files changed, 363 insertions(+), 351 deletions(-) rename desktop/src/main/java/haveno/desktop/main/offer/offerbook/{TopCryptoOfferBookView.fxml => CryptoOfferBookView.fxml} (97%) rename desktop/src/main/java/haveno/desktop/main/offer/offerbook/{TopCryptoOfferBookView.java => CryptoOfferBookView.java} (56%) create mode 100644 desktop/src/main/java/haveno/desktop/main/offer/offerbook/CryptoOfferBookViewModel.java rename desktop/src/main/java/haveno/desktop/main/offer/offerbook/{XmrOfferBookView.fxml => FiatOfferBookView.fxml} (97%) rename desktop/src/main/java/haveno/desktop/main/offer/offerbook/{XmrOfferBookView.java => FiatOfferBookView.java} (75%) rename desktop/src/main/java/haveno/desktop/main/offer/offerbook/{XmrOfferBookViewModel.java => FiatOfferBookViewModel.java} (84%) delete mode 100644 desktop/src/main/java/haveno/desktop/main/offer/offerbook/TopCryptoOfferBookViewModel.java diff --git a/core/src/main/java/haveno/core/locale/CurrencyUtil.java b/core/src/main/java/haveno/core/locale/CurrencyUtil.java index b482ea4f..45371225 100644 --- a/core/src/main/java/haveno/core/locale/CurrencyUtil.java +++ b/core/src/main/java/haveno/core/locale/CurrencyUtil.java @@ -73,14 +73,6 @@ public class CurrencyUtil { private static String baseCurrencyCode = "XMR"; - private static List getTraditionalNonFiatCurrencies() { - return Arrays.asList( - new TraditionalCurrency("XAG", "Silver"), - new TraditionalCurrency("XAU", "Gold"), - new TraditionalCurrency("XGB", "Goldback") - ); - } - // Calls to isTraditionalCurrency and isCryptoCurrency are very frequent so we use a cache of the results. // The main improvement was already achieved with using memoize for the source maps, but // the caching still reduces performance costs by about 20% for isCryptoCurrency (1752 ms vs 2121 ms) and about 50% @@ -124,6 +116,14 @@ public class CurrencyUtil { return new ArrayList<>(traditionalCurrencyMapSupplier.get().values()); } + public static List getTraditionalNonFiatCurrencies() { + return Arrays.asList( + new TraditionalCurrency("XAG", "Silver"), + new TraditionalCurrency("XAU", "Gold"), + new TraditionalCurrency("XGB", "Goldback") + ); + } + public static Collection getAllSortedTraditionalCurrencies(Comparator comparator) { return (List) getAllSortedTraditionalCurrencies().stream() .sorted(comparator) diff --git a/core/src/main/java/haveno/core/payment/PaymentAccount.java b/core/src/main/java/haveno/core/payment/PaymentAccount.java index 6a293b34..a9e03d80 100644 --- a/core/src/main/java/haveno/core/payment/PaymentAccount.java +++ b/core/src/main/java/haveno/core/payment/PaymentAccount.java @@ -139,6 +139,10 @@ public abstract class PaymentAccount implements PersistablePayload { return getSingleTradeCurrency() == null || CurrencyUtil.isFiatCurrency(getSingleTradeCurrency().getCode()); // TODO: check if trade currencies contain fiat } + public boolean isCryptoCurrency() { + return getSingleTradeCurrency() != null && CurrencyUtil.isCryptoCurrency(getSingleTradeCurrency().getCode()); + } + /////////////////////////////////////////////////////////////////////////////////////////// // PROTO BUFFER diff --git a/core/src/main/java/haveno/core/user/Preferences.java b/core/src/main/java/haveno/core/user/Preferences.java index 209dc7fb..3c09126c 100644 --- a/core/src/main/java/haveno/core/user/Preferences.java +++ b/core/src/main/java/haveno/core/user/Preferences.java @@ -566,6 +566,16 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid requestPersistence(); } + public void setBuyScreenOtherCurrencyCode(String buyScreenCurrencyCode) { + prefPayload.setBuyScreenOtherCurrencyCode(buyScreenCurrencyCode); + requestPersistence(); + } + + public void setSellScreenOtherCurrencyCode(String sellScreenCurrencyCode) { + prefPayload.setSellScreenOtherCurrencyCode(sellScreenCurrencyCode); + requestPersistence(); + } + public void setIgnoreTradersList(List ignoreTradersList) { prefPayload.setIgnoreTradersList(ignoreTradersList); requestPersistence(); diff --git a/core/src/main/java/haveno/core/user/PreferencesPayload.java b/core/src/main/java/haveno/core/user/PreferencesPayload.java index e45f1282..6d3d41f3 100644 --- a/core/src/main/java/haveno/core/user/PreferencesPayload.java +++ b/core/src/main/java/haveno/core/user/PreferencesPayload.java @@ -77,6 +77,10 @@ public final class PreferencesPayload implements PersistableEnvelope { private String buyScreenCryptoCurrencyCode; @Nullable private String sellScreenCryptoCurrencyCode; + @Nullable + private String buyScreenOtherCurrencyCode; + @Nullable + private String sellScreenOtherCurrencyCode; private int tradeStatisticsTickUnitIndex = 3; private boolean resyncSpvRequested; private boolean sortMarketCurrenciesNumerically = true; @@ -212,6 +216,8 @@ public final class PreferencesPayload implements PersistableEnvelope { Optional.ofNullable(sellScreenCurrencyCode).ifPresent(builder::setSellScreenCurrencyCode); Optional.ofNullable(buyScreenCryptoCurrencyCode).ifPresent(builder::setBuyScreenCryptoCurrencyCode); Optional.ofNullable(sellScreenCryptoCurrencyCode).ifPresent(builder::setSellScreenCryptoCurrencyCode); + Optional.ofNullable(buyScreenOtherCurrencyCode).ifPresent(builder::setBuyScreenOtherCurrencyCode); + Optional.ofNullable(sellScreenOtherCurrencyCode).ifPresent(builder::setSellScreenOtherCurrencyCode); Optional.ofNullable(selectedPaymentAccountForCreateOffer).ifPresent( account -> builder.setSelectedPaymentAccountForCreateOffer(selectedPaymentAccountForCreateOffer.toProtoMessage())); Optional.ofNullable(bridgeAddresses).ifPresent(builder::addAllBridgeAddresses); @@ -260,6 +266,8 @@ public final class PreferencesPayload implements PersistableEnvelope { ProtoUtil.stringOrNullFromProto(proto.getSellScreenCurrencyCode()), ProtoUtil.stringOrNullFromProto(proto.getBuyScreenCryptoCurrencyCode()), ProtoUtil.stringOrNullFromProto(proto.getSellScreenCryptoCurrencyCode()), + ProtoUtil.stringOrNullFromProto(proto.getBuyScreenOtherCurrencyCode()), + ProtoUtil.stringOrNullFromProto(proto.getSellScreenOtherCurrencyCode()), proto.getTradeStatisticsTickUnitIndex(), proto.getResyncSpvRequested(), proto.getSortMarketCurrenciesNumerically(), diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 45e07111..c0e99107 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -207,6 +207,7 @@ shared.crypto=Crypto shared.traditional=Traditional shared.otherAssets=other assets shared.other=Other +shared.preciousMetals=Precious Metals shared.all=All shared.edit=Edit shared.advancedOptions=Advanced options @@ -245,8 +246,8 @@ shared.taker=Taker #################################################################### mainView.menu.market=Market -mainView.menu.buy=Buy -mainView.menu.sell=Sell +mainView.menu.buyXmr=Buy XMR +mainView.menu.sellXmr=Sell XMR mainView.menu.portfolio=Portfolio mainView.menu.funds=Funds mainView.menu.support=Support diff --git a/core/src/main/resources/i18n/displayStrings_tr.properties b/core/src/main/resources/i18n/displayStrings_tr.properties index a9b123b4..a9e7e49a 100644 --- a/core/src/main/resources/i18n/displayStrings_tr.properties +++ b/core/src/main/resources/i18n/displayStrings_tr.properties @@ -245,8 +245,8 @@ shared.taker=Alıcı #################################################################### mainView.menu.market=Piyasa -mainView.menu.buy=Satın Al -mainView.menu.sell=Sat +mainView.menu.buyXmr=XMR Satın Al +mainView.menu.sellXmr=XMR Sat mainView.menu.portfolio=Portföy mainView.menu.funds=Fonlar mainView.menu.support=Destek diff --git a/desktop/src/main/java/haveno/desktop/main/MainView.java b/desktop/src/main/java/haveno/desktop/main/MainView.java index fe18d711..7290f768 100644 --- a/desktop/src/main/java/haveno/desktop/main/MainView.java +++ b/desktop/src/main/java/haveno/desktop/main/MainView.java @@ -165,8 +165,8 @@ public class MainView extends InitializableView { MainView.rootContainer.setNodeOrientation(NodeOrientation.RIGHT_TO_LEFT); ToggleButton marketButton = new NavButton(MarketView.class, Res.get("mainView.menu.market").toUpperCase()); - ToggleButton buyButton = new NavButton(BuyOfferView.class, Res.get("mainView.menu.buy").toUpperCase()); - ToggleButton sellButton = new NavButton(SellOfferView.class, Res.get("mainView.menu.sell").toUpperCase()); + ToggleButton buyButton = new NavButton(BuyOfferView.class, Res.get("mainView.menu.buyXmr").toUpperCase()); + ToggleButton sellButton = new NavButton(SellOfferView.class, Res.get("mainView.menu.sellXmr").toUpperCase()); ToggleButton portfolioButton = new NavButton(PortfolioView.class, Res.get("mainView.menu.portfolio").toUpperCase()); ToggleButton fundsButton = new NavButton(FundsView.class, Res.get("mainView.menu.funds").toUpperCase()); diff --git a/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartViewModel.java b/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartViewModel.java index fa0bcbd0..59c90088 100644 --- a/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/market/offerbook/OfferBookChartViewModel.java @@ -425,14 +425,10 @@ class OfferBookChartViewModel extends ActivatableViewModel { if (isSellOffer(direction)) { if (CurrencyUtil.isTraditionalCurrency(getCurrencyCode())) { preferences.setBuyScreenCurrencyCode(getCurrencyCode()); - } else if (!getCurrencyCode().equals(GUIUtil.TOP_CRYPTO.getCode())) { - preferences.setBuyScreenCryptoCurrencyCode(getCurrencyCode()); } } else { if (CurrencyUtil.isTraditionalCurrency(getCurrencyCode())) { preferences.setSellScreenCurrencyCode(getCurrencyCode()); - } else if (!getCurrencyCode().equals(GUIUtil.TOP_CRYPTO.getCode())) { - preferences.setSellScreenCryptoCurrencyCode(getCurrencyCode()); } } } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java index d48a1d2e..10ecb181 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferDataModel.java @@ -57,7 +57,6 @@ import java.util.Comparator; import static java.util.Comparator.comparing; import java.util.Date; import java.util.HashSet; -import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.Predicate; @@ -257,10 +256,10 @@ public abstract class MutableOfferDataModel extends OfferDataModel { private Optional getAnyPaymentAccount() { if (CurrencyUtil.isFiatCurrency(tradeCurrency.getCode())) { return paymentAccounts.stream().filter(paymentAccount1 -> paymentAccount1.isFiat()).findAny(); + } else if (CurrencyUtil.isCryptoCurrency(tradeCurrency.getCode())) { + return paymentAccounts.stream().filter(paymentAccount1 -> paymentAccount1.isCryptoCurrency()).findAny(); } else { - return paymentAccounts.stream().filter(paymentAccount1 -> !paymentAccount1.isFiat() && - paymentAccount1.getTradeCurrency().isPresent() && - !Objects.equals(paymentAccount1.getTradeCurrency().get().getCode(), GUIUtil.TOP_CRYPTO.getCode())).findAny(); + return paymentAccounts.stream().filter(paymentAccount1 -> paymentAccount1.getTradeCurrency().isPresent()).findAny(); } } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/OfferView.java b/desktop/src/main/java/haveno/desktop/main/offer/OfferView.java index 6ba827f0..fc7c0561 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/OfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/OfferView.java @@ -33,10 +33,10 @@ import haveno.desktop.common.view.View; import haveno.desktop.common.view.ViewLoader; import haveno.desktop.main.MainView; import haveno.desktop.main.offer.createoffer.CreateOfferView; -import haveno.desktop.main.offer.offerbook.XmrOfferBookView; +import haveno.desktop.main.offer.offerbook.FiatOfferBookView; import haveno.desktop.main.offer.offerbook.OfferBookView; +import haveno.desktop.main.offer.offerbook.CryptoOfferBookView; import haveno.desktop.main.offer.offerbook.OtherOfferBookView; -import haveno.desktop.main.offer.offerbook.TopCryptoOfferBookView; import haveno.desktop.main.offer.takeoffer.TakeOfferView; import haveno.desktop.util.GUIUtil; import haveno.network.p2p.P2PService; @@ -50,9 +50,9 @@ import java.util.Optional; public abstract class OfferView extends ActivatableView { - private OfferBookView xmrOfferBookView, topCryptoOfferBookView, otherOfferBookView; + private OfferBookView fiatOfferBookView, cryptoOfferBookView, otherOfferBookView; - private Tab xmrOfferBookTab, topCryptoOfferBookTab, otherOfferBookTab; + private Tab fiatOfferBookTab, cryptoOfferBookTab, otherOfferBookTab; private final ViewLoader viewLoader; private final Navigation navigation; @@ -95,17 +95,17 @@ public abstract class OfferView extends ActivatableView { tabChangeListener = (observableValue, oldValue, newValue) -> { UserThread.execute(() -> { if (newValue != null) { - if (newValue.equals(xmrOfferBookTab)) { - if (xmrOfferBookView != null) { - xmrOfferBookView.onTabSelected(true); + if (newValue.equals(fiatOfferBookTab)) { + if (fiatOfferBookView != null) { + fiatOfferBookView.onTabSelected(true); } else { - loadView(XmrOfferBookView.class, null, null); + loadView(FiatOfferBookView.class, null, null); } - } else if (newValue.equals(topCryptoOfferBookTab)) { - if (topCryptoOfferBookView != null) { - topCryptoOfferBookView.onTabSelected(true); + } else if (newValue.equals(cryptoOfferBookTab)) { + if (cryptoOfferBookView != null) { + cryptoOfferBookView.onTabSelected(true); } else { - loadView(TopCryptoOfferBookView.class, null, null); + loadView(CryptoOfferBookView.class, null, null); } } else if (newValue.equals(otherOfferBookTab)) { if (otherOfferBookView != null) { @@ -116,10 +116,10 @@ public abstract class OfferView extends ActivatableView { } } if (oldValue != null) { - if (oldValue.equals(xmrOfferBookTab) && xmrOfferBookView != null) { - xmrOfferBookView.onTabSelected(false); - } else if (oldValue.equals(topCryptoOfferBookTab) && topCryptoOfferBookView != null) { - topCryptoOfferBookView.onTabSelected(false); + if (oldValue.equals(fiatOfferBookTab) && fiatOfferBookView != null) { + fiatOfferBookView.onTabSelected(false); + } else if (oldValue.equals(cryptoOfferBookTab) && cryptoOfferBookView != null) { + cryptoOfferBookView.onTabSelected(false); } else if (oldValue.equals(otherOfferBookTab) && otherOfferBookView != null) { otherOfferBookView.onTabSelected(false); } @@ -154,14 +154,8 @@ public abstract class OfferView extends ActivatableView { root.getSelectionModel().selectedItemProperty().addListener(tabChangeListener); navigation.addListener(navigationListener); - if (xmrOfferBookView == null) { - navigation.navigateTo(MainView.class, this.getClass(), XmrOfferBookView.class); - } - - GUIUtil.updateTopCrypto(preferences); - - if (topCryptoOfferBookTab != null) { - topCryptoOfferBookTab.setText(GUIUtil.TOP_CRYPTO.getName().toUpperCase()); + if (fiatOfferBookView == null) { + navigation.navigateTo(MainView.class, this.getClass(), FiatOfferBookView.class); } } @@ -179,66 +173,65 @@ public abstract class OfferView extends ActivatableView { if (OfferBookView.class.isAssignableFrom(viewClass)) { - if (viewClass == XmrOfferBookView.class && xmrOfferBookTab != null && xmrOfferBookView != null) { + if (viewClass == FiatOfferBookView.class && fiatOfferBookTab != null && fiatOfferBookView != null) { if (childViewClass == null) { - xmrOfferBookTab.setContent(xmrOfferBookView.getRoot()); + fiatOfferBookTab.setContent(fiatOfferBookView.getRoot()); } else if (childViewClass == TakeOfferView.class) { - loadTakeViewClass(viewClass, childViewClass, xmrOfferBookTab); + loadTakeViewClass(viewClass, childViewClass, fiatOfferBookTab); } else { - loadCreateViewClass(xmrOfferBookView, viewClass, childViewClass, xmrOfferBookTab, (PaymentMethod) data); + loadCreateViewClass(fiatOfferBookView, viewClass, childViewClass, fiatOfferBookTab, (PaymentMethod) data); } - tabPane.getSelectionModel().select(xmrOfferBookTab); - } else if (viewClass == TopCryptoOfferBookView.class && topCryptoOfferBookTab != null && topCryptoOfferBookView != null) { + tabPane.getSelectionModel().select(fiatOfferBookTab); + } else if (viewClass == CryptoOfferBookView.class && cryptoOfferBookTab != null && cryptoOfferBookView != null) { if (childViewClass == null) { - topCryptoOfferBookTab.setContent(topCryptoOfferBookView.getRoot()); + cryptoOfferBookTab.setContent(cryptoOfferBookView.getRoot()); } else if (childViewClass == TakeOfferView.class) { - loadTakeViewClass(viewClass, childViewClass, topCryptoOfferBookTab); + loadTakeViewClass(viewClass, childViewClass, cryptoOfferBookTab); } else { - tradeCurrency = GUIUtil.TOP_CRYPTO; - loadCreateViewClass(topCryptoOfferBookView, viewClass, childViewClass, topCryptoOfferBookTab, (PaymentMethod) data); - } - tabPane.getSelectionModel().select(topCryptoOfferBookTab); - } else if (viewClass == OtherOfferBookView.class && otherOfferBookTab != null && otherOfferBookView != null) { - if (childViewClass == null) { - otherOfferBookTab.setContent(otherOfferBookView.getRoot()); - } else if (childViewClass == TakeOfferView.class) { - loadTakeViewClass(viewClass, childViewClass, otherOfferBookTab); - } else { - //add sanity check in case of app restart + // add sanity check in case of app restart if (CurrencyUtil.isTraditionalCurrency(tradeCurrency.getCode())) { Optional tradeCurrencyOptional = (this.direction == OfferDirection.SELL) ? CurrencyUtil.getTradeCurrency(preferences.getSellScreenCryptoCurrencyCode()) : CurrencyUtil.getTradeCurrency(preferences.getBuyScreenCryptoCurrencyCode()); tradeCurrency = tradeCurrencyOptional.isEmpty() ? OfferViewUtil.getAnyOfMainCryptoCurrencies() : tradeCurrencyOptional.get(); } + loadCreateViewClass(cryptoOfferBookView, viewClass, childViewClass, cryptoOfferBookTab, (PaymentMethod) data); + } + tabPane.getSelectionModel().select(cryptoOfferBookTab); + } else if (viewClass == OtherOfferBookView.class && otherOfferBookTab != null && otherOfferBookView != null) { + if (childViewClass == null) { + otherOfferBookTab.setContent(otherOfferBookView.getRoot()); + } else if (childViewClass == TakeOfferView.class) { + loadTakeViewClass(viewClass, childViewClass, otherOfferBookTab); + } else { loadCreateViewClass(otherOfferBookView, viewClass, childViewClass, otherOfferBookTab, (PaymentMethod) data); } tabPane.getSelectionModel().select(otherOfferBookTab); } else { - if (xmrOfferBookTab == null) { - xmrOfferBookTab = new Tab(Res.getBaseCurrencyName().toUpperCase()); - xmrOfferBookTab.setClosable(false); - topCryptoOfferBookTab = new Tab(GUIUtil.TOP_CRYPTO.getName().toUpperCase()); - topCryptoOfferBookTab.setClosable(false); - otherOfferBookTab = new Tab(Res.get("shared.other").toUpperCase()); + if (fiatOfferBookTab == null) { + fiatOfferBookTab = new Tab(Res.get("shared.fiat").toUpperCase()); + fiatOfferBookTab.setClosable(false); + cryptoOfferBookTab = new Tab(Res.get("shared.crypto").toUpperCase()); + cryptoOfferBookTab.setClosable(false); + otherOfferBookTab = new Tab(Res.get("shared.preciousMetals").toUpperCase()); otherOfferBookTab.setClosable(false); - tabPane.getTabs().addAll(xmrOfferBookTab, topCryptoOfferBookTab, otherOfferBookTab); + tabPane.getTabs().addAll(fiatOfferBookTab, cryptoOfferBookTab, otherOfferBookTab); } - if (viewClass == XmrOfferBookView.class) { - xmrOfferBookView = (XmrOfferBookView) viewLoader.load(XmrOfferBookView.class); - xmrOfferBookView.setOfferActionHandler(offerActionHandler); - xmrOfferBookView.setDirection(direction); - xmrOfferBookView.onTabSelected(true); - tabPane.getSelectionModel().select(xmrOfferBookTab); - xmrOfferBookTab.setContent(xmrOfferBookView.getRoot()); - } else if (viewClass == TopCryptoOfferBookView.class) { - topCryptoOfferBookView = (TopCryptoOfferBookView) viewLoader.load(TopCryptoOfferBookView.class); - topCryptoOfferBookView.setOfferActionHandler(offerActionHandler); - topCryptoOfferBookView.setDirection(direction); - topCryptoOfferBookView.onTabSelected(true); - tabPane.getSelectionModel().select(topCryptoOfferBookTab); - topCryptoOfferBookTab.setContent(topCryptoOfferBookView.getRoot()); + if (viewClass == FiatOfferBookView.class) { + fiatOfferBookView = (FiatOfferBookView) viewLoader.load(FiatOfferBookView.class); + fiatOfferBookView.setOfferActionHandler(offerActionHandler); + fiatOfferBookView.setDirection(direction); + fiatOfferBookView.onTabSelected(true); + tabPane.getSelectionModel().select(fiatOfferBookTab); + fiatOfferBookTab.setContent(fiatOfferBookView.getRoot()); + } else if (viewClass == CryptoOfferBookView.class) { + cryptoOfferBookView = (CryptoOfferBookView) viewLoader.load(CryptoOfferBookView.class); + cryptoOfferBookView.setOfferActionHandler(offerActionHandler); + cryptoOfferBookView.setDirection(direction); + cryptoOfferBookView.onTabSelected(true); + tabPane.getSelectionModel().select(cryptoOfferBookTab); + cryptoOfferBookTab.setContent(cryptoOfferBookView.getRoot()); } else if (viewClass == OtherOfferBookView.class) { otherOfferBookView = (OtherOfferBookView) viewLoader.load(OtherOfferBookView.class); otherOfferBookView.setOfferActionHandler(offerActionHandler); @@ -265,11 +258,7 @@ public abstract class OfferView extends ActivatableView { // in different graphs view = viewLoader.load(childViewClass); - // Invert direction for non-Fiat trade currencies -> BUY BCH is to SELL Monero - OfferDirection offerDirection = CurrencyUtil.isFiatCurrency(tradeCurrency.getCode()) ? direction : - direction == OfferDirection.BUY ? OfferDirection.SELL : OfferDirection.BUY; - - ((CreateOfferView) view).initWithData(offerDirection, tradeCurrency, offerActionHandler); + ((CreateOfferView) view).initWithData(direction, tradeCurrency, offerActionHandler); ((SelectableView) view).onTabSelected(true); @@ -329,9 +318,9 @@ public abstract class OfferView extends ActivatableView { private Class> getOfferBookViewClassFor(String currencyCode) { Class> offerBookViewClass; if (CurrencyUtil.isFiatCurrency(currencyCode)) { - offerBookViewClass = XmrOfferBookView.class; - } else if (currencyCode.equals(GUIUtil.TOP_CRYPTO.getCode())) { - offerBookViewClass = TopCryptoOfferBookView.class; + offerBookViewClass = FiatOfferBookView.class; + } else if (CurrencyUtil.isCryptoCurrency(currencyCode)) { + offerBookViewClass = CryptoOfferBookView.class; } else { offerBookViewClass = OtherOfferBookView.class; } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/OfferViewUtil.java b/desktop/src/main/java/haveno/desktop/main/offer/OfferViewUtil.java index 4f18294d..a01f2f3e 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/OfferViewUtil.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/OfferViewUtil.java @@ -22,16 +22,16 @@ import haveno.core.locale.CryptoCurrency; import haveno.core.locale.CurrencyUtil; import haveno.core.locale.Res; import haveno.core.locale.TradeCurrency; +import haveno.core.locale.TraditionalCurrency; import haveno.core.offer.Offer; import haveno.core.offer.OfferDirection; import haveno.core.xmr.wallet.XmrWalletService; import haveno.desktop.components.AutoTooltipLabel; -import haveno.desktop.main.offer.offerbook.XmrOfferBookView; +import haveno.desktop.main.offer.offerbook.FiatOfferBookView; import haveno.desktop.main.offer.offerbook.OfferBookView; +import haveno.desktop.main.offer.offerbook.CryptoOfferBookView; import haveno.desktop.main.offer.offerbook.OtherOfferBookView; -import haveno.desktop.main.offer.offerbook.TopCryptoOfferBookView; import haveno.desktop.main.overlays.popups.Popup; -import haveno.desktop.util.GUIUtil; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.geometry.VPos; @@ -44,7 +44,6 @@ import monero.daemon.model.MoneroSubmitTxResult; import org.jetbrains.annotations.NotNull; import java.util.HashMap; -import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; @@ -90,10 +89,10 @@ public class OfferViewUtil { public static Class> getOfferBookViewClass(String currencyCode) { Class> offerBookViewClazz; - if (CurrencyUtil.isTraditionalCurrency(currencyCode)) { - offerBookViewClazz = XmrOfferBookView.class; - } else if (currencyCode.equals(GUIUtil.TOP_CRYPTO.getCode())) { - offerBookViewClazz = TopCryptoOfferBookView.class; + if (CurrencyUtil.isFiatCurrency(currencyCode)) { + offerBookViewClazz = FiatOfferBookView.class; + } else if (CurrencyUtil.isCryptoCurrency(currencyCode)) { + offerBookViewClazz = CryptoOfferBookView.class; } else { offerBookViewClazz = OtherOfferBookView.class; } @@ -109,7 +108,7 @@ public class OfferViewUtil { } public static boolean isShownAsSellOffer(String currencyCode, OfferDirection direction) { - return CurrencyUtil.isFiatCurrency(currencyCode) == (direction == OfferDirection.SELL); + return direction == OfferDirection.SELL; } public static boolean isShownAsBuyOffer(Offer offer) { @@ -124,10 +123,18 @@ public class OfferViewUtil { return getMainCryptoCurrencies().findAny().get(); } + public static TradeCurrency getAnyOfOtherCurrencies() { + return getOtherCurrencies().findAny().get(); + } + @NotNull public static Stream getMainCryptoCurrencies() { - return CurrencyUtil.getMainCryptoCurrencies().stream().filter(cryptoCurrency -> - !Objects.equals(cryptoCurrency.getCode(), GUIUtil.TOP_CRYPTO.getCode())); + return CurrencyUtil.getMainCryptoCurrencies().stream(); + } + + @NotNull + public static Stream getOtherCurrencies() { + return CurrencyUtil.getTraditionalNonFiatCurrencies().stream(); } public static void submitTransactionHex(XmrWalletService xmrWalletService, diff --git a/desktop/src/main/java/haveno/desktop/main/offer/createoffer/CreateOfferView.java b/desktop/src/main/java/haveno/desktop/main/offer/createoffer/CreateOfferView.java index e30d53fd..ba9f3d31 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/createoffer/CreateOfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/createoffer/CreateOfferView.java @@ -31,8 +31,6 @@ import haveno.desktop.common.view.FxmlView; import haveno.desktop.main.offer.MutableOfferView; import haveno.desktop.main.offer.OfferView; import haveno.desktop.main.overlays.windows.OfferDetailsWindow; -import haveno.desktop.util.GUIUtil; -import java.util.Objects; import java.util.stream.Collectors; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -60,12 +58,12 @@ public class CreateOfferView extends MutableOfferView { protected ObservableList filterPaymentAccounts(ObservableList paymentAccounts) { return FXCollections.observableArrayList( paymentAccounts.stream().filter(paymentAccount -> { - if (model.getTradeCurrency().equals(GUIUtil.TOP_CRYPTO)) { - return Objects.equals(paymentAccount.getSingleTradeCurrency(), GUIUtil.TOP_CRYPTO); - } else if (CurrencyUtil.isFiatCurrency(model.getTradeCurrency().getCode())) { + if (CurrencyUtil.isFiatCurrency(model.getTradeCurrency().getCode())) { return paymentAccount.isFiat(); + } else if (CurrencyUtil.isCryptoCurrency(model.getTradeCurrency().getCode())) { + return paymentAccount.isCryptoCurrency(); } else { - return !paymentAccount.isFiat() && !Objects.equals(paymentAccount.getSingleTradeCurrency(), GUIUtil.TOP_CRYPTO); + return !paymentAccount.isFiat() && !paymentAccount.isCryptoCurrency(); } }).collect(Collectors.toList())); } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/TopCryptoOfferBookView.fxml b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/CryptoOfferBookView.fxml similarity index 97% rename from desktop/src/main/java/haveno/desktop/main/offer/offerbook/TopCryptoOfferBookView.fxml rename to desktop/src/main/java/haveno/desktop/main/offer/offerbook/CryptoOfferBookView.fxml index 45a46ca7..658f138c 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/TopCryptoOfferBookView.fxml +++ b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/CryptoOfferBookView.fxml @@ -19,7 +19,7 @@ - diff --git a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/TopCryptoOfferBookView.java b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/CryptoOfferBookView.java similarity index 56% rename from desktop/src/main/java/haveno/desktop/main/offer/offerbook/TopCryptoOfferBookView.java rename to desktop/src/main/java/haveno/desktop/main/offer/offerbook/CryptoOfferBookView.java index a6904d96..8dfdc686 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/TopCryptoOfferBookView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/CryptoOfferBookView.java @@ -33,39 +33,29 @@ import haveno.desktop.main.overlays.windows.OfferDetailsWindow; import javafx.scene.layout.GridPane; @FxmlView -public class TopCryptoOfferBookView extends OfferBookView { +public class CryptoOfferBookView extends OfferBookView { @Inject - TopCryptoOfferBookView(TopCryptoOfferBookViewModel model, - Navigation navigation, - OfferDetailsWindow offerDetailsWindow, - @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter, - PrivateNotificationManager privateNotificationManager, - @Named(Config.USE_DEV_PRIVILEGE_KEYS) boolean useDevPrivilegeKeys, - AccountAgeWitnessService accountAgeWitnessService, - SignedWitnessService signedWitnessService) { + CryptoOfferBookView(CryptoOfferBookViewModel model, + Navigation navigation, + OfferDetailsWindow offerDetailsWindow, + @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter, + PrivateNotificationManager privateNotificationManager, + @Named(Config.USE_DEV_PRIVILEGE_KEYS) boolean useDevPrivilegeKeys, + AccountAgeWitnessService accountAgeWitnessService, + SignedWitnessService signedWitnessService) { super(model, navigation, offerDetailsWindow, formatter, privateNotificationManager, useDevPrivilegeKeys, accountAgeWitnessService, signedWitnessService); } @Override protected String getMarketTitle() { return model.getDirection().equals(OfferDirection.BUY) ? - Res.get("offerbook.availableOffersToBuy", TopCryptoOfferBookViewModel.TOP_CRYPTO.getCode(), Res.getBaseCurrencyCode()) : - Res.get("offerbook.availableOffersToSell", TopCryptoOfferBookViewModel.TOP_CRYPTO.getCode(), Res.getBaseCurrencyCode()); - } - - @Override - protected void activate() { - model.onSetTradeCurrency(TopCryptoOfferBookViewModel.TOP_CRYPTO); - - super.activate(); - - currencyComboBoxContainer.setVisible(false); - currencyComboBoxContainer.setManaged(false); + Res.get("offerbook.availableOffersToBuy", Res.getBaseCurrencyCode(), Res.get("shared.crypto")) : + Res.get("offerbook.availableOffersToSell", Res.getBaseCurrencyCode(), Res.get("shared.crypto")); } @Override String getTradeCurrencyCode() { - return TopCryptoOfferBookViewModel.TOP_CRYPTO.getCode(); + return Res.getBaseCurrencyCode(); } } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/CryptoOfferBookViewModel.java b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/CryptoOfferBookViewModel.java new file mode 100644 index 00000000..e97c0855 --- /dev/null +++ b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/CryptoOfferBookViewModel.java @@ -0,0 +1,148 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package haveno.desktop.main.offer.offerbook; + +import com.google.inject.Inject; +import com.google.inject.name.Named; +import haveno.core.account.witness.AccountAgeWitnessService; +import haveno.core.api.CoreApi; +import haveno.core.locale.CryptoCurrency; +import haveno.core.locale.CurrencyUtil; +import haveno.core.locale.GlobalSettings; +import haveno.core.locale.TradeCurrency; +import haveno.core.offer.Offer; +import haveno.core.offer.OfferDirection; +import haveno.core.offer.OfferFilterService; +import haveno.core.offer.OpenOfferManager; +import haveno.core.payment.payload.PaymentMethod; +import haveno.core.provider.price.PriceFeedService; +import haveno.core.trade.ClosedTradableManager; +import haveno.core.user.Preferences; +import haveno.core.user.User; +import haveno.core.util.FormattingUtils; +import haveno.core.util.PriceUtil; +import haveno.core.util.coin.CoinFormatter; +import haveno.core.xmr.setup.WalletsSetup; +import haveno.desktop.Navigation; +import haveno.desktop.main.offer.OfferViewUtil; +import haveno.desktop.util.GUIUtil; +import haveno.network.p2p.P2PService; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; + +public class CryptoOfferBookViewModel extends OfferBookViewModel { + + @Inject + public CryptoOfferBookViewModel(User user, + OpenOfferManager openOfferManager, + OfferBook offerBook, + Preferences preferences, + WalletsSetup walletsSetup, + P2PService p2PService, + PriceFeedService priceFeedService, + ClosedTradableManager closedTradableManager, + AccountAgeWitnessService accountAgeWitnessService, + Navigation navigation, + PriceUtil priceUtil, + OfferFilterService offerFilterService, + @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter, + CoreApi coreApi) { + super(user, openOfferManager, offerBook, preferences, walletsSetup, p2PService, priceFeedService, closedTradableManager, accountAgeWitnessService, navigation, priceUtil, offerFilterService, btcFormatter, coreApi); + } + + @Override + void saveSelectedCurrencyCodeInPreferences(OfferDirection direction, String code) { + if (direction == OfferDirection.BUY) { + preferences.setBuyScreenCryptoCurrencyCode(code); + } else { + preferences.setSellScreenCryptoCurrencyCode(code); + } + } + + @Override + protected ObservableList filterPaymentMethods(ObservableList list, + TradeCurrency selectedTradeCurrency) { + return FXCollections.observableArrayList(list.stream().filter(paymentMethod -> { + return paymentMethod.isBlockchain(); + }).collect(Collectors.toList())); + } + + @Override + void fillCurrencies(ObservableList tradeCurrencies, + ObservableList allCurrencies) { + + tradeCurrencies.add(new CryptoCurrency(GUIUtil.SHOW_ALL_FLAG, "")); + tradeCurrencies.addAll(preferences.getCryptoCurrenciesAsObservable().stream() + .collect(Collectors.toList())); + tradeCurrencies.add(new CryptoCurrency(GUIUtil.EDIT_FLAG, "")); + + allCurrencies.add(new CryptoCurrency(GUIUtil.SHOW_ALL_FLAG, "")); + allCurrencies.addAll(CurrencyUtil.getAllSortedCryptoCurrencies().stream() + .collect(Collectors.toList())); + allCurrencies.add(new CryptoCurrency(GUIUtil.EDIT_FLAG, "")); + } + + @Override + Predicate getCurrencyAndMethodPredicate(OfferDirection direction, + TradeCurrency selectedTradeCurrency) { + return offerBookListItem -> { + Offer offer = offerBookListItem.getOffer(); + boolean directionResult = offer.getDirection() != direction; // offer to buy xmr appears as offer to sell in peer's offer book and vice versa + boolean currencyResult = CurrencyUtil.isCryptoCurrency(offer.getCurrencyCode()) && + (showAllTradeCurrenciesProperty.get() || + offer.getCurrencyCode().equals(selectedTradeCurrency.getCode())); + boolean paymentMethodResult = showAllPaymentMethods || + offer.getPaymentMethod().equals(selectedPaymentMethod); + boolean notMyOfferOrShowMyOffersActivated = !isMyOffer(offerBookListItem.getOffer()) || preferences.isShowOwnOffersInOfferBook(); + return directionResult && currencyResult && paymentMethodResult && notMyOfferOrShowMyOffersActivated; + }; + } + + @Override + TradeCurrency getDefaultTradeCurrency() { + TradeCurrency defaultTradeCurrency = GlobalSettings.getDefaultTradeCurrency(); + + if (CurrencyUtil.isCryptoCurrency(defaultTradeCurrency.getCode()) && + hasPaymentAccountForCurrency(defaultTradeCurrency)) { + return defaultTradeCurrency; + } + + ObservableList tradeCurrencies = FXCollections.observableArrayList(getTradeCurrencies()); + if (!tradeCurrencies.isEmpty()) { + // drop show all entry and select first currency with payment account available + tradeCurrencies.remove(0); + List sortedList = tradeCurrencies.stream().sorted((o1, o2) -> + Boolean.compare(!hasPaymentAccountForCurrency(o1), + !hasPaymentAccountForCurrency(o2))).collect(Collectors.toList()); + return sortedList.get(0); + } else { + return OfferViewUtil.getMainCryptoCurrencies().sorted((o1, o2) -> + Boolean.compare(!hasPaymentAccountForCurrency(o1), + !hasPaymentAccountForCurrency(o2))).collect(Collectors.toList()).get(0); + } + } + + @Override + String getCurrencyCodeFromPreferences(OfferDirection direction) { + return direction == OfferDirection.BUY ? preferences.getBuyScreenCryptoCurrencyCode() : + preferences.getSellScreenCryptoCurrencyCode(); + } +} \ No newline at end of file diff --git a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/XmrOfferBookView.fxml b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/FiatOfferBookView.fxml similarity index 97% rename from desktop/src/main/java/haveno/desktop/main/offer/offerbook/XmrOfferBookView.fxml rename to desktop/src/main/java/haveno/desktop/main/offer/offerbook/FiatOfferBookView.fxml index e5bad7e7..2f2b6602 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/XmrOfferBookView.fxml +++ b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/FiatOfferBookView.fxml @@ -19,7 +19,7 @@ - diff --git a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/XmrOfferBookView.java b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/FiatOfferBookView.java similarity index 75% rename from desktop/src/main/java/haveno/desktop/main/offer/offerbook/XmrOfferBookView.java rename to desktop/src/main/java/haveno/desktop/main/offer/offerbook/FiatOfferBookView.java index 07b4c561..9112ea9a 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/XmrOfferBookView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/FiatOfferBookView.java @@ -33,17 +33,17 @@ import haveno.desktop.main.overlays.windows.OfferDetailsWindow; import javafx.scene.layout.GridPane; @FxmlView -public class XmrOfferBookView extends OfferBookView { +public class FiatOfferBookView extends OfferBookView { @Inject - XmrOfferBookView(XmrOfferBookViewModel model, - Navigation navigation, - OfferDetailsWindow offerDetailsWindow, - @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter, - PrivateNotificationManager privateNotificationManager, - @Named(Config.USE_DEV_PRIVILEGE_KEYS) boolean useDevPrivilegeKeys, - AccountAgeWitnessService accountAgeWitnessService, - SignedWitnessService signedWitnessService) { + FiatOfferBookView(FiatOfferBookViewModel model, + Navigation navigation, + OfferDetailsWindow offerDetailsWindow, + @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter, + PrivateNotificationManager privateNotificationManager, + @Named(Config.USE_DEV_PRIVILEGE_KEYS) boolean useDevPrivilegeKeys, + AccountAgeWitnessService accountAgeWitnessService, + SignedWitnessService signedWitnessService) { super(model, navigation, offerDetailsWindow, formatter, privateNotificationManager, useDevPrivilegeKeys, accountAgeWitnessService, signedWitnessService); } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/XmrOfferBookViewModel.java b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/FiatOfferBookViewModel.java similarity index 84% rename from desktop/src/main/java/haveno/desktop/main/offer/offerbook/XmrOfferBookViewModel.java rename to desktop/src/main/java/haveno/desktop/main/offer/offerbook/FiatOfferBookViewModel.java index 38702cde..bea41743 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/XmrOfferBookViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/FiatOfferBookViewModel.java @@ -49,23 +49,23 @@ import java.util.stream.Collectors; import javafx.collections.FXCollections; import javafx.collections.ObservableList; -public class XmrOfferBookViewModel extends OfferBookViewModel { +public class FiatOfferBookViewModel extends OfferBookViewModel { @Inject - public XmrOfferBookViewModel(User user, - OpenOfferManager openOfferManager, - OfferBook offerBook, - Preferences preferences, - WalletsSetup walletsSetup, - P2PService p2PService, - PriceFeedService priceFeedService, - ClosedTradableManager closedTradableManager, - AccountAgeWitnessService accountAgeWitnessService, - Navigation navigation, - PriceUtil priceUtil, - OfferFilterService offerFilterService, - @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter, - CoreApi coreApi) { + public FiatOfferBookViewModel(User user, + OpenOfferManager openOfferManager, + OfferBook offerBook, + Preferences preferences, + WalletsSetup walletsSetup, + P2PService p2PService, + PriceFeedService priceFeedService, + ClosedTradableManager closedTradableManager, + AccountAgeWitnessService accountAgeWitnessService, + Navigation navigation, + PriceUtil priceUtil, + OfferFilterService offerFilterService, + @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter, + CoreApi coreApi) { super(user, openOfferManager, offerBook, preferences, walletsSetup, p2PService, priceFeedService, closedTradableManager, accountAgeWitnessService, navigation, priceUtil, offerFilterService, btcFormatter, coreApi); } @@ -141,9 +141,10 @@ public class XmrOfferBookViewModel extends OfferBookViewModel { !hasPaymentAccountForCurrency(o2))).collect(Collectors.toList()); return sortedList.get(0); } else { - return CurrencyUtil.getMainTraditionalCurrencies().stream().sorted((o1, o2) -> - Boolean.compare(!hasPaymentAccountForCurrency(o1), - !hasPaymentAccountForCurrency(o2))).collect(Collectors.toList()).get(0); + return CurrencyUtil.getMainTraditionalCurrencies().stream() + .filter(withFiatCurrency()) + .sorted((o1, o2) -> Boolean.compare(!hasPaymentAccountForCurrency(o1), !hasPaymentAccountForCurrency(o2))) + .collect(Collectors.toList()).get(0); } } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OtherOfferBookView.java b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OtherOfferBookView.java index 71d18d50..14d1f835 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OtherOfferBookView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OtherOfferBookView.java @@ -37,25 +37,25 @@ public class OtherOfferBookView extends OfferBookView filterPaymentMethods(ObservableList list, TradeCurrency selectedTradeCurrency) { return FXCollections.observableArrayList(list.stream().filter(paymentMethod -> { - if (paymentMethod.isBlockchain()) return true; if (paymentMethod.getSupportedAssetCodes() == null) return true; for (String assetCode : paymentMethod.getSupportedAssetCodes()) { if (!CurrencyUtil.isFiatCurrency(assetCode)) return true; @@ -95,20 +93,13 @@ public class OtherOfferBookViewModel extends OfferBookViewModel { @Override void fillCurrencies(ObservableList tradeCurrencies, ObservableList allCurrencies) { - tradeCurrencies.add(new CryptoCurrency(GUIUtil.SHOW_ALL_FLAG, "")); - tradeCurrencies.addAll(preferences.getCryptoCurrenciesAsObservable().stream() - .filter(withoutTopCrypto()) - .collect(Collectors.toList())); tradeCurrencies.addAll(CurrencyUtil.getMainTraditionalCurrencies().stream() .filter(withoutFiatCurrency()) .collect(Collectors.toList())); tradeCurrencies.add(new CryptoCurrency(GUIUtil.EDIT_FLAG, "")); allCurrencies.add(new CryptoCurrency(GUIUtil.SHOW_ALL_FLAG, "")); - allCurrencies.addAll(CurrencyUtil.getAllSortedCryptoCurrencies().stream() - .filter(withoutTopCrypto()) - .collect(Collectors.toList())); allCurrencies.addAll(CurrencyUtil.getMainTraditionalCurrencies().stream() .filter(withoutFiatCurrency()) .collect(Collectors.toList())); @@ -120,12 +111,9 @@ public class OtherOfferBookViewModel extends OfferBookViewModel { TradeCurrency selectedTradeCurrency) { return offerBookListItem -> { Offer offer = offerBookListItem.getOffer(); - // BUY Crypto is actually SELL Monero - boolean directionResult = offer.getDirection() == direction; - boolean currencyResult = !CurrencyUtil.isFiatCurrency(offer.getCurrencyCode()) && - ((showAllTradeCurrenciesProperty.get() && - !offer.getCurrencyCode().equals(GUIUtil.TOP_CRYPTO.getCode())) || - offer.getCurrencyCode().equals(selectedTradeCurrency.getCode())); + boolean directionResult = offer.getDirection() != direction; + boolean currencyResult = CurrencyUtil.isTraditionalCurrency(offer.getCurrencyCode()) && !CurrencyUtil.isFiatCurrency(offer.getCurrencyCode()) && + (showAllTradeCurrenciesProperty.get() || offer.getCurrencyCode().equals(selectedTradeCurrency.getCode())); boolean paymentMethodResult = showAllPaymentMethods || offer.getPaymentMethod().equals(selectedPaymentMethod); boolean notMyOfferOrShowMyOffersActivated = !isMyOffer(offerBookListItem.getOffer()) || preferences.isShowOwnOffersInOfferBook(); @@ -137,8 +125,8 @@ public class OtherOfferBookViewModel extends OfferBookViewModel { TradeCurrency getDefaultTradeCurrency() { TradeCurrency defaultTradeCurrency = GlobalSettings.getDefaultTradeCurrency(); - if (!CurrencyUtil.isTraditionalCurrency(defaultTradeCurrency.getCode()) && - !defaultTradeCurrency.equals(GUIUtil.TOP_CRYPTO) && + if (CurrencyUtil.isTraditionalCurrency(defaultTradeCurrency.getCode()) && + !CurrencyUtil.isFiatCurrency(defaultTradeCurrency.getCode()) && hasPaymentAccountForCurrency(defaultTradeCurrency)) { return defaultTradeCurrency; } @@ -152,22 +140,19 @@ public class OtherOfferBookViewModel extends OfferBookViewModel { !hasPaymentAccountForCurrency(o2))).collect(Collectors.toList()); return sortedList.get(0); } else { - return OfferViewUtil.getMainCryptoCurrencies().sorted((o1, o2) -> - Boolean.compare(!hasPaymentAccountForCurrency(o1), - !hasPaymentAccountForCurrency(o2))).collect(Collectors.toList()).get(0); + return CurrencyUtil.getMainTraditionalCurrencies().stream() + .filter(withoutFiatCurrency()) + .sorted((o1, o2) -> Boolean.compare(!hasPaymentAccountForCurrency(o1), !hasPaymentAccountForCurrency(o2))) + .collect(Collectors.toList()).get(0); } } @Override String getCurrencyCodeFromPreferences(OfferDirection direction) { - return direction == OfferDirection.BUY ? preferences.getBuyScreenCryptoCurrencyCode() : - preferences.getSellScreenCryptoCurrencyCode(); - } + // validate if previous stored currencies are Traditional ones + String currencyCode = direction == OfferDirection.BUY ? preferences.getBuyScreenOtherCurrencyCode() : preferences.getSellScreenOtherCurrencyCode(); - @NotNull - private Predicate withoutTopCrypto() { - return cryptoCurrency -> - !cryptoCurrency.equals(GUIUtil.TOP_CRYPTO); + return CurrencyUtil.isTraditionalCurrency(currencyCode) ? currencyCode : null; } @NotNull diff --git a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/TopCryptoOfferBookViewModel.java b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/TopCryptoOfferBookViewModel.java deleted file mode 100644 index 37cdaa12..00000000 --- a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/TopCryptoOfferBookViewModel.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package haveno.desktop.main.offer.offerbook; - -import com.google.inject.Inject; -import com.google.inject.name.Named; -import haveno.core.account.witness.AccountAgeWitnessService; -import haveno.core.api.CoreApi; -import haveno.core.locale.TradeCurrency; -import haveno.core.offer.Offer; -import haveno.core.offer.OfferDirection; -import haveno.core.offer.OfferFilterService; -import haveno.core.offer.OpenOfferManager; -import haveno.core.payment.payload.PaymentMethod; -import haveno.core.provider.price.PriceFeedService; -import haveno.core.trade.ClosedTradableManager; -import haveno.core.user.Preferences; -import haveno.core.user.User; -import haveno.core.util.FormattingUtils; -import haveno.core.util.PriceUtil; -import haveno.core.util.coin.CoinFormatter; -import haveno.core.xmr.setup.WalletsSetup; -import haveno.desktop.Navigation; -import haveno.desktop.util.GUIUtil; -import haveno.network.p2p.P2PService; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; - -public class TopCryptoOfferBookViewModel extends OfferBookViewModel { - - public static TradeCurrency TOP_CRYPTO = GUIUtil.TOP_CRYPTO; - - @Inject - public TopCryptoOfferBookViewModel(User user, - OpenOfferManager openOfferManager, - OfferBook offerBook, - Preferences preferences, - WalletsSetup walletsSetup, - P2PService p2PService, - PriceFeedService priceFeedService, - ClosedTradableManager closedTradableManager, - AccountAgeWitnessService accountAgeWitnessService, - Navigation navigation, - PriceUtil priceUtil, - OfferFilterService offerFilterService, - @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter, - CoreApi coreApi) { - super(user, openOfferManager, offerBook, preferences, walletsSetup, p2PService, priceFeedService, closedTradableManager, accountAgeWitnessService, navigation, priceUtil, offerFilterService, btcFormatter, coreApi); - } - - @Override - protected void activate() { - super.activate(); - TOP_CRYPTO = GUIUtil.TOP_CRYPTO; - } - - @Override - void saveSelectedCurrencyCodeInPreferences(OfferDirection direction, String code) { - // No need to store anything as it is just one Crypto offers anyway - } - - @Override - protected ObservableList filterPaymentMethods(ObservableList list, - TradeCurrency selectedTradeCurrency) { - return FXCollections.observableArrayList(list.stream().filter(PaymentMethod::isBlockchain).collect(Collectors.toList())); - } - - @Override - void fillCurrencies(ObservableList tradeCurrencies, - ObservableList allCurrencies) { - tradeCurrencies.add(TOP_CRYPTO); - allCurrencies.add(TOP_CRYPTO); - } - - @Override - Predicate getCurrencyAndMethodPredicate(OfferDirection direction, - TradeCurrency selectedTradeCurrency) { - return offerBookListItem -> { - Offer offer = offerBookListItem.getOffer(); - // BUY Crypto is actually SELL Bitcoin - boolean directionResult = offer.getDirection() == direction; - boolean currencyResult = offer.getCurrencyCode().equals(TOP_CRYPTO.getCode()); - boolean paymentMethodResult = showAllPaymentMethods || - offer.getPaymentMethod().equals(selectedPaymentMethod); - boolean notMyOfferOrShowMyOffersActivated = !isMyOffer(offerBookListItem.getOffer()) || preferences.isShowOwnOffersInOfferBook(); - return directionResult && currencyResult && paymentMethodResult && notMyOfferOrShowMyOffersActivated; - }; - } - - @Override - TradeCurrency getDefaultTradeCurrency() { - return TOP_CRYPTO; - } - - @Override - String getCurrencyCodeFromPreferences(OfferDirection direction) { - return TOP_CRYPTO.getCode(); - } -} diff --git a/desktop/src/main/java/haveno/desktop/util/GUIUtil.java b/desktop/src/main/java/haveno/desktop/util/GUIUtil.java index 2c314ea5..a5a0b86d 100644 --- a/desktop/src/main/java/haveno/desktop/util/GUIUtil.java +++ b/desktop/src/main/java/haveno/desktop/util/GUIUtil.java @@ -134,8 +134,6 @@ public class GUIUtil { private static Preferences preferences; - public static TradeCurrency TOP_CRYPTO = CurrencyUtil.getTradeCurrency("BTC").get(); - public static void setPreferences(Preferences preferences) { GUIUtil.preferences = preferences; } @@ -1033,12 +1031,4 @@ public class GUIUtil { columnConstraints2.setHgrow(Priority.ALWAYS); gridPane.getColumnConstraints().addAll(columnConstraints1, columnConstraints2); } - - public static void updateTopCrypto(Preferences preferences) { - TradeCurrency tradeCurrency = preferences.getPreferredTradeCurrency(); - if (CurrencyUtil.isTraditionalCurrency(tradeCurrency.getCode())) { - return; - } - TOP_CRYPTO = tradeCurrency; - } } diff --git a/desktop/src/test/java/haveno/desktop/main/offer/offerbook/OfferBookViewModelTest.java b/desktop/src/test/java/haveno/desktop/main/offer/offerbook/OfferBookViewModelTest.java index bce59e65..3b648533 100644 --- a/desktop/src/test/java/haveno/desktop/main/offer/offerbook/OfferBookViewModelTest.java +++ b/desktop/src/test/java/haveno/desktop/main/offer/offerbook/OfferBookViewModelTest.java @@ -241,7 +241,7 @@ public class OfferBookViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); - final OfferBookViewModel model = new XmrOfferBookViewModel(null, null, offerBook, empty, null, null, null, + final OfferBookViewModel model = new FiatOfferBookViewModel(null, null, offerBook, empty, null, null, null, null, null, null, getPriceUtil(), null, coinFormatter, null); assertEquals(0, model.maxPlacesForAmount.intValue()); } @@ -255,7 +255,7 @@ public class OfferBookViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); - final OfferBookViewModel model = new XmrOfferBookViewModel(user, openOfferManager, offerBook, empty, null, null, null, + final OfferBookViewModel model = new FiatOfferBookViewModel(user, openOfferManager, offerBook, empty, null, null, null, null, null, null, getPriceUtil(), null, coinFormatter, null); model.activate(); @@ -273,7 +273,7 @@ public class OfferBookViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); - final OfferBookViewModel model = new XmrOfferBookViewModel(user, openOfferManager, offerBook, empty, null, null, null, + final OfferBookViewModel model = new FiatOfferBookViewModel(user, openOfferManager, offerBook, empty, null, null, null, null, null, null, getPriceUtil(), null, coinFormatter, null); model.activate(); @@ -292,7 +292,7 @@ public class OfferBookViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); - final OfferBookViewModel model = new XmrOfferBookViewModel(null, null, offerBook, empty, null, null, null, + final OfferBookViewModel model = new FiatOfferBookViewModel(null, null, offerBook, empty, null, null, null, null, null, null, getPriceUtil(), null, coinFormatter, null); assertEquals(0, model.maxPlacesForVolume.intValue()); } @@ -306,7 +306,7 @@ public class OfferBookViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); - final OfferBookViewModel model = new XmrOfferBookViewModel(user, openOfferManager, offerBook, empty, null, null, null, + final OfferBookViewModel model = new FiatOfferBookViewModel(user, openOfferManager, offerBook, empty, null, null, null, null, null, null, getPriceUtil(), null, coinFormatter, null); model.activate(); @@ -324,7 +324,7 @@ public class OfferBookViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); - final OfferBookViewModel model = new XmrOfferBookViewModel(user, openOfferManager, offerBook, empty, null, null, null, + final OfferBookViewModel model = new FiatOfferBookViewModel(user, openOfferManager, offerBook, empty, null, null, null, null, null, null, getPriceUtil(), null, coinFormatter, null); model.activate(); @@ -342,7 +342,7 @@ public class OfferBookViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); - final OfferBookViewModel model = new XmrOfferBookViewModel(null, null, offerBook, empty, null, null, null, + final OfferBookViewModel model = new FiatOfferBookViewModel(null, null, offerBook, empty, null, null, null, null, null, null, getPriceUtil(), null, coinFormatter, null); assertEquals(0, model.maxPlacesForPrice.intValue()); } @@ -356,7 +356,7 @@ public class OfferBookViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); - final OfferBookViewModel model = new XmrOfferBookViewModel(user, openOfferManager, offerBook, empty, null, null, null, + final OfferBookViewModel model = new FiatOfferBookViewModel(user, openOfferManager, offerBook, empty, null, null, null, null, null, null, getPriceUtil(), null, coinFormatter, null); model.activate(); @@ -374,7 +374,7 @@ public class OfferBookViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); - final OfferBookViewModel model = new XmrOfferBookViewModel(null, null, offerBook, empty, null, null, null, + final OfferBookViewModel model = new FiatOfferBookViewModel(null, null, offerBook, empty, null, null, null, null, null, null, getPriceUtil(), null, coinFormatter, null); assertEquals(0, model.maxPlacesForMarketPriceMargin.intValue()); } @@ -409,7 +409,7 @@ public class OfferBookViewModelTest { item4.getOffer().setPriceFeedService(priceFeedService); offerBookListItems.addAll(item1, item2); - final OfferBookViewModel model = new XmrOfferBookViewModel(user, openOfferManager, offerBook, empty, null, null, priceFeedService, + final OfferBookViewModel model = new FiatOfferBookViewModel(user, openOfferManager, offerBook, empty, null, null, priceFeedService, null, null, null, getPriceUtil(), null, coinFormatter, null); model.activate(); @@ -430,7 +430,7 @@ public class OfferBookViewModelTest { when(offerBook.getOfferBookListItems()).thenReturn(offerBookListItems); when(priceFeedService.getMarketPrice(anyString())).thenReturn(new MarketPrice("USD", 12684.0450, Instant.now().getEpochSecond(), true)); - final OfferBookViewModel model = new XmrOfferBookViewModel(user, openOfferManager, offerBook, empty, null, null, null, + final OfferBookViewModel model = new FiatOfferBookViewModel(user, openOfferManager, offerBook, empty, null, null, null, null, null, null, getPriceUtil(), null, coinFormatter, null); final OfferBookListItem item = make(xmrBuyItem.but( diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto index 00b44736..db5aa49d 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -1742,6 +1742,8 @@ message PreferencesPayload { bool split_offer_output = 62; bool use_sound_for_notifications = 63; bool use_sound_for_notifications_initialized = 64; + string buy_screen_other_currency_code = 65; + string sell_screen_other_currency_code = 66; } message AutoConfirmSettings { From 2a45ebe5658aa650f36510dcd28b927d58a33e16 Mon Sep 17 00:00:00 2001 From: woodser Date: Wed, 6 Nov 2024 08:06:53 -0500 Subject: [PATCH 22/46] refactor buy/sell tab labels #1351 --- .../resources/i18n/displayStrings.properties | 4 +++- .../i18n/displayStrings_cs.properties | 4 ++++ .../i18n/displayStrings_de.properties | 4 ++++ .../i18n/displayStrings_es.properties | 4 ++++ .../i18n/displayStrings_fa.properties | 4 ++++ .../i18n/displayStrings_fr.properties | 4 ++++ .../i18n/displayStrings_it.properties | 4 ++++ .../i18n/displayStrings_ja.properties | 4 ++++ .../i18n/displayStrings_pt-br.properties | 4 ++++ .../i18n/displayStrings_pt.properties | 4 ++++ .../i18n/displayStrings_ru.properties | 4 ++++ .../i18n/displayStrings_th.properties | 4 ++++ .../i18n/displayStrings_tr.properties | 5 ++++- .../i18n/displayStrings_vi.properties | 4 ++++ .../i18n/displayStrings_zh-hans.properties | 4 ++++ .../i18n/displayStrings_zh-hant.properties | 4 ++++ .../src/main/java/haveno/desktop/haveno.css | 8 +++++++ .../desktop/main/offer/BuyOfferView.java | 7 ++++++ .../haveno/desktop/main/offer/OfferView.java | 21 ++++++++++++++---- .../desktop/main/offer/SellOfferView.java | 7 ++++++ .../main/offer/offerbook/OfferBookView.java | 22 +++---------------- 21 files changed, 105 insertions(+), 25 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index c0e99107..34499884 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -376,6 +376,8 @@ offerbook.timeSinceSigning.tooltip.checkmark.buyXmr=buy XMR from a signed accoun offerbook.timeSinceSigning.tooltip.checkmark.wait=wait a minimum of {0} days offerbook.timeSinceSigning.tooltip.learnMore=Learn more offerbook.xmrAutoConf=Is auto-confirm enabled +offerbook.buyXmrWith=Buy XMR with: +offerbook.sellXmrFor=Sell XMR for: offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n\ {0} days later, the initial limit of {1} is lifted and your account can sign other peers'' payment accounts. @@ -390,7 +392,7 @@ offerbook.volume={0} (min - max) offerbook.deposit=Deposit XMR (%) offerbook.deposit.help=Deposit paid by each trader to guarantee the trade. Will be returned when the trade is completed. -offerbook.createNewOffer=Create new offer to {0} {1} +offerbook.createNewOffer=Create offer to {0} {1} offerbook.createOfferDisabled.tooltip=You can only create one offer at a time offerbook.takeOfferButton.tooltip=Take offer for {0} diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index 18b25963..3cd127ac 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -192,6 +192,7 @@ shared.iConfirm=Potvrzuji shared.openURL=Otevřené {0} shared.fiat=Fiat shared.crypto=Krypto +shared.preciousMetals=Drahé kovy shared.all=Vše shared.edit=Upravit shared.advancedOptions=Pokročilé možnosti @@ -348,6 +349,8 @@ offerbook.timeSinceSigning.info.banned=účet byl zablokován offerbook.timeSinceSigning.daysSinceSigning={0} dní offerbook.timeSinceSigning.daysSinceSigning.long={0} od podpisu offerbook.xmrAutoConf=Je automatické potvrzení povoleno +offerbook.buyXmrWith=Kupte XMR za: +offerbook.sellXmrFor=Prodat XMR za: offerbook.timeSinceSigning.help=Když úspěšně dokončíte obchod s uživatelem, který má podepsaný platební účet, je váš platební účet podepsán.\n{0} dní později se počáteční limit {1} zruší a váš účet může podepisovat platební účty ostatních uživatelů. offerbook.timeSinceSigning.notSigned=Dosud nepodepsáno @@ -362,6 +365,7 @@ offerbook.nrOffers=Počet nabídek: {0} offerbook.volume={0} (min - max) offerbook.deposit=Kauce XMR (%) offerbook.deposit.help=Kauce zaplacená každým obchodníkem k zajištění obchodu. Bude vrácena po dokončení obchodu. +offerbook.createNewOffer=Vytvořit nabídku pro {0} {1} offerbook.createOfferToBuy=Vytvořit novou nabídku k nákupu {0} offerbook.createOfferToSell=Vytvořit novou nabídku k prodeji {0} diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index 3c32e2c3..798445be 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -192,6 +192,7 @@ shared.iConfirm=Ich bestätige shared.openURL=Öffne {0} shared.fiat=Fiat shared.crypto=Crypto +shared.preciousMetals=Edelmetalle shared.all=Alle shared.edit=Bearbeiten shared.advancedOptions=Erweiterte Optionen @@ -348,6 +349,8 @@ offerbook.timeSinceSigning.info.banned=Konto wurde geblockt offerbook.timeSinceSigning.daysSinceSigning={0} Tage offerbook.timeSinceSigning.daysSinceSigning.long={0} seit der Unterzeichnung offerbook.xmrAutoConf=Automatische Bestätigung aktiviert +offerbook.buyXmrWith=XMR kaufen mit: +offerbook.sellXmrFor=XMR verkaufen für: offerbook.timeSinceSigning.help=Wenn Sie einen Trade mit einem Partner erfolgreich abschließen, der ein unterzeichnetes Zahlungskonto hat, wird Ihr Zahlungskonto unterzeichnet.\n{0} Tage später wird das anfängliche Limit von {1} aufgehoben und Ihr Konto kann die Zahlungskonten anderer Partner unterzeichnen. offerbook.timeSinceSigning.notSigned=Noch nicht unterzeichnet @@ -362,6 +365,7 @@ offerbook.nrOffers=Anzahl der Angebote: {0} offerbook.volume={0} (min - max) offerbook.deposit=Kaution XMR (%) offerbook.deposit.help=Kaution die von beiden Handelspartnern bezahlt werden muss, um den Handel abzusichern. Wird zurückgezahlt, wenn der Handel erfolgreich abgeschlossen wurde. +offerbook.createNewOffer=Erstelle Angebot an {0} {1} offerbook.createOfferToBuy=Neues Angebot erstellen, um {0} zu kaufen offerbook.createOfferToSell=Neues Angebot erstellen, um {0} zu verkaufen diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index fb1d4d0b..d123b919 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -192,6 +192,7 @@ shared.iConfirm=Confirmo shared.openURL=Abrir {0} shared.fiat=Fiat shared.crypto=Cripto +shared.preciousMetals=Metales Preciosos shared.all=Todos shared.edit=Editar shared.advancedOptions=Opciones avanzadas @@ -348,6 +349,8 @@ offerbook.timeSinceSigning.info.banned=La cuenta fue bloqueada offerbook.timeSinceSigning.daysSinceSigning={0} días offerbook.timeSinceSigning.daysSinceSigning.long={0} desde el firmado offerbook.xmrAutoConf=¿Está habilitada la confirmación automática? +offerbook.buyXmrWith=Compra XMR con: +offerbook.sellXmrFor=Vender XMR por: offerbook.timeSinceSigning.help=Cuando complete con éxito un intercambio con un par que tenga una cuenta de pago firmada, su cuenta de pago es firmada.\n{0} días después, el límite inicial de {1} se eleva y su cuenta puede firmar tras cuentas de pago. offerbook.timeSinceSigning.notSigned=No firmada aún @@ -362,6 +365,7 @@ offerbook.nrOffers=Número de ofertas: {0} offerbook.volume={0} (min - max) offerbook.deposit=Depósito en XMR (%) offerbook.deposit.help=Depósito pagado por cada comerciante para garantizar el intercambio. Será devuelto al acabar el intercambio. +offerbook.createNewOffer=Crear oferta a {0} {1} offerbook.createOfferToBuy=Crear nueva oferta para comprar {0} offerbook.createOfferToSell=Crear nueva oferta para vender {0} diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index 53a2b2be..ee230421 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -192,6 +192,7 @@ shared.iConfirm=تایید می‌کنم shared.openURL=باز {0} shared.fiat=فیات shared.crypto=کریپتو +shared.preciousMetals=فلزات گرانبها shared.all=همه shared.edit=ویرایش shared.advancedOptions=گزینه‌های پیشرفته @@ -348,6 +349,8 @@ offerbook.timeSinceSigning.info.banned=account was banned offerbook.timeSinceSigning.daysSinceSigning={0} روز offerbook.timeSinceSigning.daysSinceSigning.long={0} since signing offerbook.xmrAutoConf=Is auto-confirm enabled +offerbook.buyXmrWith=با XMR خرید کنید: +offerbook.sellXmrFor=فروش XMR برای: offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n{0} days later, the initial limit of {1} is lifted and your account can sign other peers'' payment accounts. offerbook.timeSinceSigning.notSigned=Not signed yet @@ -362,6 +365,7 @@ offerbook.nrOffers=تعداد پیشنهادها: {0} offerbook.volume={0} (حداقل - حداکثر) offerbook.deposit=Deposit XMR (%) offerbook.deposit.help=Deposit paid by each trader to guarantee the trade. Will be returned when the trade is completed. +offerbook.createNewOffer=پیشنهاد ایجاد کنید به {0} {1} offerbook.createOfferToBuy=پیشنهاد جدید برای خرید {0} ایجاد کن offerbook.createOfferToSell=پیشنهاد جدید برای فروش {0} ایجاد کن diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index ec42105c..5130e608 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -192,6 +192,7 @@ shared.iConfirm=Je confirme shared.openURL=Ouvert {0} shared.fiat=Fiat shared.crypto=Crypto +shared.preciousMetals=Métaux précieux shared.all=Tout shared.edit=Modifier shared.advancedOptions=Options avancées @@ -348,6 +349,8 @@ offerbook.timeSinceSigning.info.banned=Ce compte a été banni offerbook.timeSinceSigning.daysSinceSigning={0} jours offerbook.timeSinceSigning.daysSinceSigning.long={0} depuis la signature offerbook.xmrAutoConf=Est-ce-que la confirmation automatique est activée +offerbook.buyXmrWith=Acheter XMR avec : +offerbook.sellXmrFor=Vendre XMR pour : offerbook.timeSinceSigning.help=Lorsque vous effectuez avec succès une transaction avec un pair disposant d''un compte de paiement signé, votre compte de paiement est signé.\n{0} Jours plus tard, la limite initiale de {1} est levée et votre compte peut signer les comptes de paiement d''un autre pair. offerbook.timeSinceSigning.notSigned=Pas encore signé @@ -362,6 +365,7 @@ offerbook.nrOffers=Nombre d''ordres: {0} offerbook.volume={0} (min - max) offerbook.deposit=Déposer XMR (%) offerbook.deposit.help=Les deux parties à la transaction ont payé un dépôt pour assurer que la transaction se déroule normalement. Ce montant sera remboursé une fois la transaction terminée. +offerbook.createNewOffer=Créer une offre à {0} {1} offerbook.createOfferToBuy=Créer un nouvel ordre d''achat pour {0} offerbook.createOfferToSell=Créer un nouvel ordre de vente pour {0} diff --git a/core/src/main/resources/i18n/displayStrings_it.properties b/core/src/main/resources/i18n/displayStrings_it.properties index d663400d..8b202733 100644 --- a/core/src/main/resources/i18n/displayStrings_it.properties +++ b/core/src/main/resources/i18n/displayStrings_it.properties @@ -192,6 +192,7 @@ shared.iConfirm=Confermo shared.openURL=Aperti {0} shared.fiat=Fiat shared.crypto=Crypto +shared.preciousMetals=Metalli Preziosi shared.all=Tutti shared.edit=Modifica shared.advancedOptions=Opzioni avanzate @@ -348,6 +349,8 @@ offerbook.timeSinceSigning.info.banned= \nl'account è stato bannato offerbook.timeSinceSigning.daysSinceSigning={0} giorni offerbook.timeSinceSigning.daysSinceSigning.long={0} dalla firma offerbook.xmrAutoConf=Is auto-confirm enabled +offerbook.buyXmrWith=Compra XMR con: +offerbook.sellXmrFor=Vendi XMR per: offerbook.timeSinceSigning.help=Quando completi correttamente un'operazione con un peer che ha un account di pagamento firmato, il tuo account di pagamento viene firmato.\n{0} giorni dopo, il limite iniziale di {1} viene alzato e il tuo account può firmare account di pagamento di altri peer. offerbook.timeSinceSigning.notSigned=Non ancora firmato @@ -362,6 +365,7 @@ offerbook.nrOffers=N. di offerte: {0} offerbook.volume={0} (min - max) offerbook.deposit=Deposit XMR (%) offerbook.deposit.help=Deposit paid by each trader to guarantee the trade. Will be returned when the trade is completed. +offerbook.createNewOffer=Crea offerta per {0} {1} offerbook.createOfferToBuy=Crea una nuova offerta per comprare {0} offerbook.createOfferToSell=Crea una nuova offerta per vendere {0} diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index 372906c2..76174c73 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -192,6 +192,7 @@ shared.iConfirm=確認します shared.openURL={0} をオープン shared.fiat=法定通貨 shared.crypto=暗号通貨 +shared.preciousMetals=貴金属 shared.all=全て shared.edit=編集 shared.advancedOptions=高度なオプション @@ -348,6 +349,8 @@ offerbook.timeSinceSigning.info.banned=このアカウントは禁止されま offerbook.timeSinceSigning.daysSinceSigning={0}日 offerbook.timeSinceSigning.daysSinceSigning.long=署名する後から {0} offerbook.xmrAutoConf=自動確認は有効されますか? +offerbook.buyXmrWith=XMRを購入: +offerbook.sellXmrFor=XMRを売る: offerbook.timeSinceSigning.help=署名された支払いアカウントを持っているピアと成功にトレードすると、自身の支払いアカウントも署名されることになります。\n{0} 日後に、{1} という初期の制限は解除され、他のピアの支払いアカウントを署名できるようになります。 offerbook.timeSinceSigning.notSigned=まだ署名されていません @@ -362,6 +365,7 @@ offerbook.nrOffers=オファー数: {0} offerbook.volume={0} (下限 - 上限) offerbook.deposit=XMRの敷金(%) offerbook.deposit.help=トレードを保証するため、両方の取引者が支払う敷金。トレードが完了されたら、返還されます。 +offerbook.createNewOffer={0} {1}にオファーを作成する offerbook.createOfferToBuy={0} を購入するオファーを作成 offerbook.createOfferToSell={0} を売却するオファーを作成 diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index b521753f..7dc51d9a 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -192,6 +192,7 @@ shared.iConfirm=Eu confirmo shared.openURL=Aberto {0} shared.fiat=Fiat shared.crypto=Cripto +shared.preciousMetals=Metais Preciosos shared.all=Todos shared.edit=Editar shared.advancedOptions=Opções avançadas @@ -351,6 +352,8 @@ offerbook.timeSinceSigning.info.banned=conta foi banida offerbook.timeSinceSigning.daysSinceSigning={0} dias offerbook.timeSinceSigning.daysSinceSigning.long={0} desde a assinatura offerbook.xmrAutoConf=Is auto-confirm enabled +offerbook.buyXmrWith=Compre XMR com: +offerbook.sellXmrFor=Venda XMR por: offerbook.timeSinceSigning.help=Quando você completa uma negociação bem sucedida com um par que tem uma conta de pagamento assinada, a sua conta de pagamento é assinada.\n{0} dias depois, o limite inicial de {1} é levantado e sua conta pode assinar as contas de pagamento de outros pares. offerbook.timeSinceSigning.notSigned=Ainda não assinada @@ -365,6 +368,7 @@ offerbook.nrOffers=N.º de ofertas: {0} offerbook.volume={0} (mín. - máx.) offerbook.deposit=Deposit XMR (%) offerbook.deposit.help=Deposit paid by each trader to guarantee the trade. Will be returned when the trade is completed. +offerbook.createNewOffer=Criar oferta para {0} {1} offerbook.createOfferToBuy=Criar oferta para comprar {0} offerbook.createOfferToSell=Criar oferta para vender {0} diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index 3567f188..25bd0342 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -192,6 +192,7 @@ shared.iConfirm=Eu confirmo shared.openURL=Abrir {0} shared.fiat=Moeda fiduciária shared.crypto=Cripto +shared.preciousMetals=TODO shared.all=Tudo shared.edit=Editar shared.advancedOptions=Opções avançadas @@ -348,6 +349,8 @@ offerbook.timeSinceSigning.info.banned=account was banned offerbook.timeSinceSigning.daysSinceSigning={0} dias offerbook.timeSinceSigning.daysSinceSigning.long={0} desde a assinatura offerbook.xmrAutoConf=Is auto-confirm enabled +offerbook.buyXmrWith=Compre XMR com: +offerbook.sellXmrFor=Venda XMR por: offerbook.timeSinceSigning.help=Quando você completa com sucesso um negócio com um par que tenha uma conta de pagamento assinada, a sua conta de pagamento é assinada .\n{0} dias depois, o limite inicial de {1} é aumentado e a sua conta pode assinar contas de pagamento de outros pares. offerbook.timeSinceSigning.notSigned=Ainda não assinada @@ -362,6 +365,7 @@ offerbook.nrOffers=Nº de ofertas: {0} offerbook.volume={0} (mín - máx) offerbook.deposit=Deposit XMR (%) offerbook.deposit.help=Deposit paid by each trader to guarantee the trade. Will be returned when the trade is completed. +offerbook.createNewOffer=Criar oferta para {0} {1} offerbook.createOfferToBuy=Criar nova oferta para comprar {0} offerbook.createOfferToSell=Criar nova oferta para vender {0} diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index 9e67e9e7..1c1fffbf 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -192,6 +192,7 @@ shared.iConfirm=Подтверждаю shared.openURL=Открыть {0} shared.fiat=Нац. валюта shared.crypto=Криптовалюта +shared.preciousMetals=Драгоценные металлы shared.all=Все shared.edit=Редактировать shared.advancedOptions=Дополнительные настройки @@ -348,6 +349,8 @@ offerbook.timeSinceSigning.info.banned=account was banned offerbook.timeSinceSigning.daysSinceSigning={0} дн. offerbook.timeSinceSigning.daysSinceSigning.long={0} since signing offerbook.xmrAutoConf=Is auto-confirm enabled +offerbook.buyXmrWith=Купить XMR с помощью: +offerbook.sellXmrFor=Продать XMR за: offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n{0} days later, the initial limit of {1} is lifted and your account can sign other peers'' payment accounts. offerbook.timeSinceSigning.notSigned=Not signed yet @@ -362,6 +365,7 @@ offerbook.nrOffers=Кол-во предложений: {0} offerbook.volume={0} (мин. ⁠— макс.) offerbook.deposit=Deposit XMR (%) offerbook.deposit.help=Deposit paid by each trader to guarantee the trade. Will be returned when the trade is completed. +offerbook.createNewOffer=Создать предложение для {0} {1} offerbook.createOfferToBuy=Создать новое предложение на покупку {0} offerbook.createOfferToSell=Создать новое предложение на продажу {0} diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index 34f0b3dd..44778d95 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -192,6 +192,7 @@ shared.iConfirm=ฉันยืนยัน shared.openURL=เปิด {0} shared.fiat=คำสั่ง shared.crypto=คริปโต +shared.preciousMetals=โลหะมีค่า shared.all=ทั้งหมด shared.edit=แก้ไข shared.advancedOptions=ทางเลือกขั้นสูง @@ -348,6 +349,8 @@ offerbook.timeSinceSigning.info.banned=account was banned offerbook.timeSinceSigning.daysSinceSigning={0} วัน offerbook.timeSinceSigning.daysSinceSigning.long={0} since signing offerbook.xmrAutoConf=Is auto-confirm enabled +offerbook.buyXmrWith=ซื้อ XMR ด้วย: +offerbook.sellXmrFor=ขาย XMR สำหรับ: offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n{0} days later, the initial limit of {1} is lifted and your account can sign other peers'' payment accounts. offerbook.timeSinceSigning.notSigned=Not signed yet @@ -362,6 +365,7 @@ offerbook.nrOffers=No. ของข้อเสนอ: {0} offerbook.volume={0} (ต่ำสุด - สูงสุด) offerbook.deposit=Deposit XMR (%) offerbook.deposit.help=Deposit paid by each trader to guarantee the trade. Will be returned when the trade is completed. +offerbook.createNewOffer=สร้างข้อเสนอให้กับ {0} {1} offerbook.createOfferToBuy=Create new offer to buy {0} offerbook.createOfferToSell=Create new offer to sell {0} diff --git a/core/src/main/resources/i18n/displayStrings_tr.properties b/core/src/main/resources/i18n/displayStrings_tr.properties index a9e7e49a..cc20d534 100644 --- a/core/src/main/resources/i18n/displayStrings_tr.properties +++ b/core/src/main/resources/i18n/displayStrings_tr.properties @@ -204,6 +204,7 @@ shared.iConfirm=Onaylıyorum shared.openURL={0}'i aç shared.fiat=Fiat shared.crypto=Kripto +shared.preciousMetals=Değerli Madenler shared.traditional=Nakit shared.otherAssets=diğer varlıklar shared.other=Diğer @@ -372,6 +373,8 @@ offerbook.timeSinceSigning.tooltip.checkmark.buyXmr=imzalı bir hesaptan XMR al offerbook.timeSinceSigning.tooltip.checkmark.wait=minimal {0} gün bekleyin offerbook.timeSinceSigning.tooltip.learnMore=Daha fazla bilgi edin offerbook.xmrAutoConf=Otomatik onay etkin mi +offerbook.buyXmrWith=XMR satın al: +offerbook.sellXmrFor=XMR'i şunlar için satın: offerbook.timeSinceSigning.help=Bir imzalı ödeme hesabı olan bir eş ile başarılı bir şekilde işlem yaptığınızda, ödeme hesabınız imzalanır.\n\ {0} gün sonra, başlangıç limiti {1} kaldırılır ve hesabınız diğer eşlerin ödeme hesaplarını imzalayabilir. @@ -386,7 +389,7 @@ offerbook.volume={0} (min - maks) offerbook.deposit=Mevduat XMR (%) offerbook.deposit.help=Her yatırımcı tarafından işlemi garanti altına almak için ödenen mevduat. İşlem tamamlandığında geri verilecektir. -offerbook.createNewOffer=teklif aç {0} {1} +offerbook.createNewOffer=Teklif oluştur {0} {1} offerbook.createOfferDisabled.tooltip=Bir seferde sadece bir teklif oluşturabilirsiniz offerbook.takeOfferButton.tooltip=Teklifi al {0} diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index 33f8bf35..a7bf554b 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -192,6 +192,7 @@ shared.iConfirm=Tôi xác nhận shared.openURL=Mở {0} shared.fiat=Tiền pháp định shared.crypto=Tiền mã hóa +shared.preciousMetals=Kim loại quý shared.all=Tất cả shared.edit=Chỉnh sửa shared.advancedOptions=Tùy chọn nâng cao @@ -348,6 +349,8 @@ offerbook.timeSinceSigning.info.banned=account was banned offerbook.timeSinceSigning.daysSinceSigning={0} ngày offerbook.timeSinceSigning.daysSinceSigning.long={0} since signing offerbook.xmrAutoConf=Is auto-confirm enabled +offerbook.buyXmrWith=Mua XMR với: +offerbook.sellXmrFor=Bán XMR để: offerbook.timeSinceSigning.help=When you successfully complete a trade with a peer who has a signed payment account, your payment account is signed.\n{0} days later, the initial limit of {1} is lifted and your account can sign other peers'' payment accounts. offerbook.timeSinceSigning.notSigned=Not signed yet @@ -362,6 +365,7 @@ offerbook.nrOffers=Số chào giá: {0} offerbook.volume={0} (min - max) offerbook.deposit=Deposit XMR (%) offerbook.deposit.help=Deposit paid by each trader to guarantee the trade. Will be returned when the trade is completed. +offerbook.createNewOffer=Tạo ưu đãi cho {0} {1} offerbook.createOfferToBuy=Tạo chào giá mua mới {0} offerbook.createOfferToSell=Tạo chào giá bán mới {0} diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index bc2d39b1..ac6e618d 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -192,6 +192,7 @@ shared.iConfirm=我确认 shared.openURL=打开 {0} shared.fiat=法定货币 shared.crypto=加密 +shared.preciousMetals=贵金属 shared.all=全部 shared.edit=编辑 shared.advancedOptions=高级选项 @@ -348,6 +349,8 @@ offerbook.timeSinceSigning.info.banned=账户已被封禁 offerbook.timeSinceSigning.daysSinceSigning={0} 天 offerbook.timeSinceSigning.daysSinceSigning.long=自验证{0} offerbook.xmrAutoConf=是否开启自动确认 +offerbook.buyXmrWith=使用以下方式购买 XMR: +offerbook.sellXmrFor=出售 XMR 以换取: offerbook.timeSinceSigning.help=当您成功地完成与拥有已验证付款帐户的伙伴交易时,您的付款帐户已验证。\n{0} 天后,最初的 {1} 的限制解除以及你的账户可以验证其他人的付款账户。 offerbook.timeSinceSigning.notSigned=尚未验证 @@ -362,6 +365,7 @@ offerbook.nrOffers=报价数量:{0} offerbook.volume={0}(最小 - 最大) offerbook.deposit=XMR 保证金(%) offerbook.deposit.help=交易双方均已支付保证金确保这个交易正常进行。这会在交易完成时退还。 +offerbook.createNewOffer=創建報價給 {0} {1} offerbook.createOfferToBuy=创建新的报价来买入 {0} offerbook.createOfferToSell=创建新的报价来卖出 {0} diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index 39a95951..40ae3bc5 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -192,6 +192,7 @@ shared.iConfirm=我確認 shared.openURL=打開 {0} shared.fiat=法定貨幣 shared.crypto=加密 +shared.preciousMetals=貴金屬 shared.all=全部 shared.edit=編輯 shared.advancedOptions=高級選項 @@ -348,6 +349,8 @@ offerbook.timeSinceSigning.info.banned=賬户已被封禁 offerbook.timeSinceSigning.daysSinceSigning={0} 天 offerbook.timeSinceSigning.daysSinceSigning.long=自驗證{0} offerbook.xmrAutoConf=是否開啟自動確認 +offerbook.buyXmrWith=購買 XMR 使用: +offerbook.sellXmrFor=出售 XMR 以換取: offerbook.timeSinceSigning.help=當您成功地完成與擁有已驗證付款帳户的夥伴交易時,您的付款帳户已驗證。\n{0} 天后,最初的 {1} 的限制解除以及你的賬户可以驗證其他人的付款賬户。 offerbook.timeSinceSigning.notSigned=尚未驗證 @@ -362,6 +365,7 @@ offerbook.nrOffers=報價數量:{0} offerbook.volume={0}(最小 - 最大) offerbook.deposit=XMR 保證金(%) offerbook.deposit.help=交易雙方均已支付保證金確保這個交易正常進行。這會在交易完成時退還。 +offerbook.createNewOffer=創建報價給 {0} {1} offerbook.createOfferToBuy=創建新的報價來買入 {0} offerbook.createOfferToSell=創建新的報價來賣出 {0} diff --git a/desktop/src/main/java/haveno/desktop/haveno.css b/desktop/src/main/java/haveno/desktop/haveno.css index 95b57a3a..fc92b1c3 100644 --- a/desktop/src/main/java/haveno/desktop/haveno.css +++ b/desktop/src/main/java/haveno/desktop/haveno.css @@ -1237,6 +1237,14 @@ textfield */ -jfx-rippler-fill: -fx-accent; } +.tab:disabled .jfx-rippler { + -jfx-rippler-fill: none !important; +} + +.tab:disabled .tab-label { + -fx-cursor: default !important; +} + .jfx-tab-pane .headers-region .tab .tab-container .tab-close-button > .jfx-svg-glyph { -fx-shape: "M810 274l-238 238 238 238-60 60-238-238-238 238-60-60 238-238-238-238 60-60 238 238 238-238z"; -jfx-size: 9; diff --git a/desktop/src/main/java/haveno/desktop/main/offer/BuyOfferView.java b/desktop/src/main/java/haveno/desktop/main/offer/BuyOfferView.java index c5def765..67591cbc 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/BuyOfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/BuyOfferView.java @@ -18,6 +18,8 @@ package haveno.desktop.main.offer; import com.google.inject.Inject; + +import haveno.core.locale.Res; import haveno.core.offer.OfferDirection; import haveno.core.user.Preferences; import haveno.core.user.User; @@ -42,4 +44,9 @@ public class BuyOfferView extends OfferView { p2PService, OfferDirection.BUY); } + + @Override + protected String getOfferLabel() { + return Res.get("offerbook.buyXmrWith"); + } } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/OfferView.java b/desktop/src/main/java/haveno/desktop/main/offer/OfferView.java index fc7c0561..6f6aba7c 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/OfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/OfferView.java @@ -41,6 +41,7 @@ import haveno.desktop.main.offer.takeoffer.TakeOfferView; import haveno.desktop.util.GUIUtil; import haveno.network.p2p.P2PService; import javafx.beans.value.ChangeListener; +import javafx.scene.control.Label; import javafx.scene.control.Tab; import javafx.scene.control.TabPane; import org.jetbrains.annotations.NotNull; @@ -52,7 +53,7 @@ public abstract class OfferView extends ActivatableView { private OfferBookView fiatOfferBookView, cryptoOfferBookView, otherOfferBookView; - private Tab fiatOfferBookTab, cryptoOfferBookTab, otherOfferBookTab; + private Tab labelTab, fiatOfferBookTab, cryptoOfferBookTab, otherOfferBookTab; private final ViewLoader viewLoader; private final Navigation navigation; @@ -165,6 +166,8 @@ public abstract class OfferView extends ActivatableView { root.getSelectionModel().selectedItemProperty().removeListener(tabChangeListener); } + protected abstract String getOfferLabel(); + private void loadView(Class viewClass, Class childViewClass, @Nullable Object data) { @@ -209,14 +212,24 @@ public abstract class OfferView extends ActivatableView { tabPane.getSelectionModel().select(otherOfferBookTab); } else { if (fiatOfferBookTab == null) { + + // add preceding label tab + labelTab = new Tab(); + labelTab.setDisable(true); + labelTab.setContent(new Label()); + labelTab.setClosable(false); + Label offerLabel = new Label(getOfferLabel()); // use overlay for label for custom formatting + offerLabel.getStyleClass().add("titled-group-bg-label"); + offerLabel.setStyle("-fx-font-size: 1.4em;"); + labelTab.setGraphic(offerLabel); + fiatOfferBookTab = new Tab(Res.get("shared.fiat").toUpperCase()); fiatOfferBookTab.setClosable(false); cryptoOfferBookTab = new Tab(Res.get("shared.crypto").toUpperCase()); cryptoOfferBookTab.setClosable(false); - otherOfferBookTab = new Tab(Res.get("shared.preciousMetals").toUpperCase()); + otherOfferBookTab = new Tab(Res.get("shared.other").toUpperCase()); otherOfferBookTab.setClosable(false); - - tabPane.getTabs().addAll(fiatOfferBookTab, cryptoOfferBookTab, otherOfferBookTab); + tabPane.getTabs().addAll(labelTab, fiatOfferBookTab, cryptoOfferBookTab, otherOfferBookTab); } if (viewClass == FiatOfferBookView.class) { fiatOfferBookView = (FiatOfferBookView) viewLoader.load(FiatOfferBookView.class); diff --git a/desktop/src/main/java/haveno/desktop/main/offer/SellOfferView.java b/desktop/src/main/java/haveno/desktop/main/offer/SellOfferView.java index 931f70ac..fde0e57f 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/SellOfferView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/SellOfferView.java @@ -18,6 +18,8 @@ package haveno.desktop.main.offer; import com.google.inject.Inject; + +import haveno.core.locale.Res; import haveno.core.offer.OfferDirection; import haveno.core.user.Preferences; import haveno.core.user.User; @@ -42,4 +44,9 @@ public class SellOfferView extends OfferView { p2PService, OfferDirection.SELL); } + + @Override + protected String getOfferLabel() { + return Res.get("offerbook.sellXmrFor"); + } } diff --git a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java index 0a37b36e..6d9575b0 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/offerbook/OfferBookView.java @@ -52,7 +52,6 @@ import haveno.desktop.components.ColoredDecimalPlacesWithZerosText; import haveno.desktop.components.HyperlinkWithIcon; import haveno.desktop.components.InfoAutoTooltipLabel; import haveno.desktop.components.PeerInfoIconTrading; -import haveno.desktop.components.TitledGroupBg; import haveno.desktop.main.MainView; import haveno.desktop.main.account.AccountView; import haveno.desktop.main.account.content.cryptoaccounts.CryptoAccountsView; @@ -67,7 +66,6 @@ import haveno.desktop.main.portfolio.PortfolioView; import haveno.desktop.main.portfolio.editoffer.EditOfferView; import haveno.desktop.util.FormBuilder; import haveno.desktop.util.GUIUtil; -import haveno.desktop.util.Layout; import haveno.network.p2p.NodeAddress; import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.beans.value.ChangeListener; @@ -106,7 +104,6 @@ import java.util.Comparator; import java.util.Map; import java.util.Optional; -import static haveno.desktop.util.FormBuilder.addTitledGroupBg; import static haveno.desktop.util.FormBuilder.addTopLabelAutoToolTipTextField; abstract public class OfferBookView extends ActivatableViewAndModel { @@ -119,7 +116,6 @@ abstract public class OfferBookView currencyComboBox; private AutocompleteComboBox paymentMethodComboBox; private AutoTooltipButton createOfferButton; @@ -170,18 +166,10 @@ abstract public class OfferBookView> currencyBoxTuple = FormBuilder.addTopLabelAutocompleteComboBox( Res.get("offerbook.filterByCurrency")); @@ -202,7 +190,7 @@ abstract public class OfferBookView(); @@ -332,10 +320,6 @@ abstract public class OfferBookView offerCounts = OfferViewUtil.isShownAsBuyOffer(model.getDirection(), model.getSelectedTradeCurrency()) ? model.getSellOfferCounts() : model.getBuyOfferCounts(); currencyComboBox.setCellFactory(GUIUtil.getTradeCurrencyCellFactory(Res.get("shared.oneOffer"), From b31758e884a74deae9ea6c115723a3092220cfd4 Mon Sep 17 00:00:00 2001 From: XMRZombie Date: Sun, 10 Nov 2024 13:39:12 +0000 Subject: [PATCH 23/46] improve robustness and clarity of SingleThreadExecutorUtils.java (#1378) --- .../util/SingleThreadExecutorUtils.java | 45 +++++++++++++++---- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/common/src/main/java/haveno/common/util/SingleThreadExecutorUtils.java b/common/src/main/java/haveno/common/util/SingleThreadExecutorUtils.java index 6e336c00..d9af624c 100644 --- a/common/src/main/java/haveno/common/util/SingleThreadExecutorUtils.java +++ b/common/src/main/java/haveno/common/util/SingleThreadExecutorUtils.java @@ -11,8 +11,8 @@ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public * License for more details. * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . + * You should have received a copy of the GNU Affero General Public + * License along with Bisq. If not, see . */ package haveno.common.util; @@ -25,38 +25,67 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; +/** + * Utility class for creating single-threaded executors. + */ public class SingleThreadExecutorUtils { + + private SingleThreadExecutorUtils() { + // Prevent instantiation + } + public static ExecutorService getSingleThreadExecutor(Class aClass) { - String name = aClass.getSimpleName(); - return getSingleThreadExecutor(name); + validateClass(aClass); + return getSingleThreadExecutor(aClass.getSimpleName()); } public static ExecutorService getNonDaemonSingleThreadExecutor(Class aClass) { - String name = aClass.getSimpleName(); - return getSingleThreadExecutor(name, false); + validateClass(aClass); + return getSingleThreadExecutor(aClass.getSimpleName(), false); } public static ExecutorService getSingleThreadExecutor(String name) { + validateName(name); return getSingleThreadExecutor(name, true); } public static ListeningExecutorService getSingleThreadListeningExecutor(String name) { + validateName(name); return MoreExecutors.listeningDecorator(getSingleThreadExecutor(name)); } public static ExecutorService getSingleThreadExecutor(ThreadFactory threadFactory) { + validateThreadFactory(threadFactory); return Executors.newSingleThreadExecutor(threadFactory); } private static ExecutorService getSingleThreadExecutor(String name, boolean isDaemonThread) { - final ThreadFactory threadFactory = getThreadFactory(name, isDaemonThread); + ThreadFactory threadFactory = getThreadFactory(name, isDaemonThread); return Executors.newSingleThreadExecutor(threadFactory); } private static ThreadFactory getThreadFactory(String name, boolean isDaemonThread) { return new ThreadFactoryBuilder() - .setNameFormat(name) + .setNameFormat(name + "-%d") .setDaemon(isDaemonThread) .build(); } + + private static void validateClass(Class aClass) { + if (aClass == null) { + throw new IllegalArgumentException("Class must not be null."); + } + } + + private static void validateName(String name) { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("Name must not be null or empty."); + } + } + + private static void validateThreadFactory(ThreadFactory threadFactory) { + if (threadFactory == null) { + throw new IllegalArgumentException("ThreadFactory must not be null."); + } + } } From 6730d023d614fc0fcbda79b6846df24d325b6806 Mon Sep 17 00:00:00 2001 From: woodser Date: Sun, 10 Nov 2024 08:44:59 -0500 Subject: [PATCH 24/46] describe how payment account details are stored and shared #1359 (#1387) Co-authored-by: rafau --- core/src/main/resources/i18n/displayStrings.properties | 1 + core/src/main/resources/i18n/displayStrings_cs.properties | 1 + core/src/main/resources/i18n/displayStrings_de.properties | 1 + core/src/main/resources/i18n/displayStrings_es.properties | 1 + core/src/main/resources/i18n/displayStrings_fa.properties | 1 + core/src/main/resources/i18n/displayStrings_fr.properties | 1 + core/src/main/resources/i18n/displayStrings_it.properties | 1 + core/src/main/resources/i18n/displayStrings_ja.properties | 1 + core/src/main/resources/i18n/displayStrings_pt-br.properties | 1 + core/src/main/resources/i18n/displayStrings_pt.properties | 1 + core/src/main/resources/i18n/displayStrings_ru.properties | 1 + core/src/main/resources/i18n/displayStrings_th.properties | 1 + core/src/main/resources/i18n/displayStrings_tr.properties | 1 + core/src/main/resources/i18n/displayStrings_vi.properties | 1 + .../src/main/resources/i18n/displayStrings_zh-hans.properties | 1 + .../src/main/resources/i18n/displayStrings_zh-hant.properties | 1 + .../content/traditionalaccounts/TraditionalAccountsView.java | 4 +++- 17 files changed, 19 insertions(+), 1 deletion(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 34499884..6e9ad25f 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -150,6 +150,7 @@ shared.addNewAccount=Add new account shared.ExportAccounts=Export Accounts shared.importAccounts=Import Accounts shared.createNewAccount=Create new account +shared.createNewAccountDescription=Your account details are stored locally on your device and shared only with your trading peer and the arbitrator if a dispute is opened. shared.saveNewAccount=Save new account shared.selectedAccount=Selected account shared.deleteAccount=Delete account diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index 3cd127ac..c47bb440 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -139,6 +139,7 @@ shared.addNewAccount=Přidat nový účet shared.ExportAccounts=Exportovat účty shared.importAccounts=Importovat účty shared.createNewAccount=Vytvořit nový účet +shared.createNewAccountDescription=Vaše údaje o účtu jsou uloženy místně na vašem zařízení a sdíleny pouze s vaším obchodním partnerem a rozhodcem, pokud dojde k otevření sporu. shared.saveNewAccount=Uložit nový účet shared.selectedAccount=Vybraný účet shared.deleteAccount=Smazat účet diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index 798445be..5e691f53 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -139,6 +139,7 @@ shared.addNewAccount=Neues Konto hinzufügen shared.ExportAccounts=Konten exportieren shared.importAccounts=Konten importieren shared.createNewAccount=Neues Konto erstellen +shared.createNewAccountDescription=Ihre Kontodaten werden lokal auf Ihrem Gerät gespeichert und nur mit Ihrem Handelspartner und dem Schiedsrichter geteilt, wenn ein Streitfall eröffnet wird. shared.saveNewAccount=Neues Konto speichern shared.selectedAccount=Konto auswählen shared.deleteAccount=Konto löschen diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index d123b919..26be43ee 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -139,6 +139,7 @@ shared.addNewAccount=Añadir una nueva cuenta shared.ExportAccounts=Exportar cuentas shared.importAccounts=Importar cuentas shared.createNewAccount=Crear nueva cuenta +shared.createNewAccountDescription=Los detalles de su cuenta se almacenan localmente en su dispositivo y se comparten solo con su contraparte comercial y el árbitro si se abre una disputa. shared.saveNewAccount=Guardar nueva cuenta shared.selectedAccount=Cuenta seleccionada shared.deleteAccount=Borrar cuenta diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index ee230421..171f3929 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -139,6 +139,7 @@ shared.addNewAccount=افزودن حساب جدید shared.ExportAccounts=صادر کردن حساب‌ها shared.importAccounts=وارد کردن حساب‌ها shared.createNewAccount=ایجاد حساب جدید +shared.createNewAccountDescription=جزئیات حساب شما به‌طور محلی بر روی دستگاه شما ذخیره شده و تنها با هم‌تجارت شما و داور در صورت باز شدن یک اختلاف به اشتراک گذاشته می‌شود. shared.saveNewAccount=ذخیره‌ی حساب جدید shared.selectedAccount=حساب انتخاب شده shared.deleteAccount=حذف حساب diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index 5130e608..1dfb4e2b 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -139,6 +139,7 @@ shared.addNewAccount=Ajouter un nouveau compte shared.ExportAccounts=Exporter les comptes shared.importAccounts=Importer les comptes shared.createNewAccount=Créer un nouveau compte +shared.createNewAccountDescription=Les détails de votre compte sont stockés localement sur votre appareil et partagés uniquement avec votre pair de trading et l'arbitre si un litige est ouvert. shared.saveNewAccount=Sauvegarder un nouveau compte shared.selectedAccount=Sélectionner un compte shared.deleteAccount=Supprimer le compte diff --git a/core/src/main/resources/i18n/displayStrings_it.properties b/core/src/main/resources/i18n/displayStrings_it.properties index 8b202733..6964d462 100644 --- a/core/src/main/resources/i18n/displayStrings_it.properties +++ b/core/src/main/resources/i18n/displayStrings_it.properties @@ -139,6 +139,7 @@ shared.addNewAccount=Aggiungi nuovo account shared.ExportAccounts=Esporta Account shared.importAccounts=Importa Account shared.createNewAccount=Crea nuovo account +shared.createNewAccountDescription=I dettagli del tuo account sono memorizzati localmente sul tuo dispositivo e condivisi solo con il tuo partner commerciale e l'arbitro se viene aperta una disputa. shared.saveNewAccount=Salva nuovo account shared.selectedAccount=Account selezionato shared.deleteAccount=Elimina account diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index 76174c73..1229dbe1 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -139,6 +139,7 @@ shared.addNewAccount=アカウントを追加 shared.ExportAccounts=アカウントをエクスポート shared.importAccounts=アカウントをインポート shared.createNewAccount=新しいアカウントを作る +shared.createNewAccountDescription=あなたのアカウント詳細は、デバイスにローカルに保存され、取引相手および紛争が発生した場合には仲裁人とのみ共有されます。 shared.saveNewAccount=新しいアカウントを保存する shared.selectedAccount=選択したアカウント shared.deleteAccount=アカウントを削除 diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index 7dc51d9a..7c79f993 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -139,6 +139,7 @@ shared.addNewAccount=Adicionar conta nova shared.ExportAccounts=Exportar Contas shared.importAccounts=Importar Contas shared.createNewAccount=Criar nova conta +shared.createNewAccountDescription=Os detalhes da sua conta são armazenados localmente no seu dispositivo e compartilhados apenas com seu parceiro de negociação e o árbitro, caso uma disputa seja aberta. shared.saveNewAccount=Salvar nova conta shared.selectedAccount=Conta selecionada shared.deleteAccount=Apagar conta diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index 25bd0342..fd6d3c62 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -139,6 +139,7 @@ shared.addNewAccount=Adicionar uma nova conta shared.ExportAccounts=Exportar Contas shared.importAccounts=Importar Contas shared.createNewAccount=Criar nova conta +shared.createNewAccountDescription=Os detalhes da sua conta são armazenados localmente no seu dispositivo e compartilhados apenas com seu parceiro de negociação e o árbitro, caso uma disputa seja aberta. shared.saveNewAccount=Guardar nova conta shared.selectedAccount=Conta selecionada shared.deleteAccount=Apagar conta diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index 1c1fffbf..5f976feb 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -139,6 +139,7 @@ shared.addNewAccount=Добавить новый счёт shared.ExportAccounts=Экспортировать счета shared.importAccounts=Импортировать счета shared.createNewAccount=Создать новый счёт +shared.createNewAccountDescription=Данные вашей учетной записи хранятся локально на вашем устройстве и передаются только вашему торговому партнеру и арбитру, если открывается спор. shared.saveNewAccount=Сохранить новый счёт shared.selectedAccount=Выбранный счёт shared.deleteAccount=Удалить счёт diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index 44778d95..15486e22 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -139,6 +139,7 @@ shared.addNewAccount=เพิ่มบัญชีใหม่ shared.ExportAccounts=บัญชีส่งออก shared.importAccounts=บัญชีนำเข้า shared.createNewAccount=สร้างบัญชีใหม่ +shared.createNewAccountDescription=รายละเอียดบัญชีของคุณถูกจัดเก็บไว้ในอุปกรณ์ของคุณและจะแบ่งปันเฉพาะกับคู่ค้าของคุณและผู้ตัดสินหากมีการเปิดข้อพิพาท shared.saveNewAccount=บันทึกบัญชีใหม่ shared.selectedAccount=บัญชีที่เลือก shared.deleteAccount=ลบบัญชี diff --git a/core/src/main/resources/i18n/displayStrings_tr.properties b/core/src/main/resources/i18n/displayStrings_tr.properties index cc20d534..d50061a1 100644 --- a/core/src/main/resources/i18n/displayStrings_tr.properties +++ b/core/src/main/resources/i18n/displayStrings_tr.properties @@ -150,6 +150,7 @@ shared.addNewAccount=Yeni hesap ekle shared.ExportAccounts=Hesapları Dışa Aktar shared.importAccounts=Hesapları İçe Aktar shared.createNewAccount=Yeni hesap oluştur +shared.createNewAccountDescription=Hesap bilgileriniz yerel olarak cihazınızda saklanır ve yalnızca ticaret ortağınızla ve bir anlaşmazlık açılırsa hakemle paylaşılır. shared.saveNewAccount=Yeni hesabı kaydet shared.selectedAccount=Seçilen hesap shared.deleteAccount=Hesabı sil diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index a7bf554b..c3d7548e 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -139,6 +139,7 @@ shared.addNewAccount=Thêm tài khoản mới shared.ExportAccounts=Truy xuất tài khoản shared.importAccounts=Truy nhập tài khoản shared.createNewAccount=Tạo tài khoản mới +shared.createNewAccountDescription=Thông tin tài khoản của bạn được lưu trữ cục bộ trên thiết bị của bạn và chỉ được chia sẻ với đối tác giao dịch của bạn và trọng tài nếu xảy ra tranh chấp. shared.saveNewAccount=Lưu tài khoản mới shared.selectedAccount=Tài khoản được chọn shared.deleteAccount=Xóa tài khoản diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index ac6e618d..f8af6af2 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -139,6 +139,7 @@ shared.addNewAccount=添加新的账户 shared.ExportAccounts=导出账户 shared.importAccounts=导入账户 shared.createNewAccount=创建新的账户 +shared.createNewAccountDescription=您的账户详情存储在您的设备上,仅与您的交易对手和仲裁员在出现争议时共享。 shared.saveNewAccount=保存新的账户 shared.selectedAccount=选中的账户 shared.deleteAccount=删除账户 diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index 40ae3bc5..1dd25d90 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -139,6 +139,7 @@ shared.addNewAccount=添加新的賬户 shared.ExportAccounts=導出賬户 shared.importAccounts=導入賬户 shared.createNewAccount=創建新的賬户 +shared.createNewAccountDescription=您的帳戶詳細資料儲存在您的裝置上,僅在開啟爭議時與您的交易夥伴和仲裁者分享。 shared.saveNewAccount=保存新的賬户 shared.selectedAccount=選中的賬户 shared.deleteAccount=刪除賬户 diff --git a/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java b/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java index ae2b0a1b..191bce5d 100644 --- a/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java +++ b/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsView.java @@ -136,6 +136,7 @@ import haveno.desktop.util.FormBuilder; import static haveno.desktop.util.FormBuilder.add2ButtonsAfterGroup; import static haveno.desktop.util.FormBuilder.add3ButtonsAfterGroup; import static haveno.desktop.util.FormBuilder.addTitledGroupBg; +import static haveno.desktop.util.FormBuilder.addLabel; import static haveno.desktop.util.FormBuilder.addTopLabelListView; import haveno.desktop.util.GUIUtil; import haveno.desktop.util.Layout; @@ -463,8 +464,9 @@ public class TraditionalAccountsView extends PaymentAccountsView Date: Tue, 5 Nov 2024 12:15:42 -0500 Subject: [PATCH 25/46] show seller's receive address for crypto in trade step view --- .../pendingtrades/steps/seller/SellerStep3View.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java index a492c744..c5cfcbcd 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java @@ -203,10 +203,8 @@ public class SellerStep3View extends TradeStepView { .orElse(""); if (myPaymentAccountPayload instanceof AssetAccountPayload) { - if (myPaymentDetails.isEmpty()) { - // Not expected - myPaymentDetails = ((AssetAccountPayload) myPaymentAccountPayload).getAddress(); - } + // for crypto always display the receiving address + myPaymentDetails = ((AssetAccountPayload) myPaymentAccountPayload).getAddress(); peersPaymentDetails = peersPaymentAccountPayload != null ? ((AssetAccountPayload) peersPaymentAccountPayload).getAddress() : "NA"; myTitle = Res.get("portfolio.pending.step3_seller.yourAddress", currencyName); From 7094dfcc09649581371dc4d7cbe6e5b6e56ea25e Mon Sep 17 00:00:00 2001 From: woodser Date: Tue, 15 Oct 2024 22:31:00 -0400 Subject: [PATCH 26/46] always poll trade wallet on sync and poll --- core/src/main/java/haveno/core/trade/Trade.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index 5f51e8ea..994c6112 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -2438,7 +2438,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { } } - if (pollWallet) pollWallet(); + if (pollWallet) doPollWallet(); } catch (Exception e) { ThreadUtils.execute(() -> requestSwitchToNextBestConnection(sourceConnection), getId()); throw e; From 329fa1c39a61ab39565b7a2e4a31891dd8fad6c9 Mon Sep 17 00:00:00 2001 From: woodser Date: Wed, 16 Oct 2024 07:19:51 -0400 Subject: [PATCH 27/46] set poll in progress from single caller at a time --- core/src/main/java/haveno/core/trade/Trade.java | 14 ++++++++++++-- .../haveno/core/xmr/wallet/XmrWalletService.java | 16 +++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index 994c6112..dbab49cd 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -2500,10 +2500,18 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { } private void doPollWallet() { + + // skip if shut down started if (isShutDownStarted) return; + + // set poll in progress + boolean pollInProgressSet = false; synchronized (pollLock) { + if (!pollInProgress) pollInProgressSet = true; pollInProgress = true; } + + // poll wallet try { // skip if payout unlocked @@ -2628,8 +2636,10 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { } } } finally { - synchronized (pollLock) { - pollInProgress = false; + if (pollInProgressSet) { + synchronized (pollLock) { + pollInProgress = false; + } } requestSaveWallet(); } diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index 1a9ead35..6ffd6537 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -1837,10 +1837,18 @@ public class XmrWalletService extends XmrWalletBase { } private void doPollWallet(boolean updateTxs) { + + // skip if shut down started + if (isShutDownStarted) return; + + // set poll in progress + boolean pollInProgressSet = false; synchronized (pollLock) { + if (!pollInProgress) pollInProgressSet = true; pollInProgress = true; } - if (isShutDownStarted) return; + + // poll wallet try { // skip if daemon not synced @@ -1903,8 +1911,10 @@ public class XmrWalletService extends XmrWalletBase { //e.printStackTrace(); } } finally { - synchronized (pollLock) { - pollInProgress = false; + if (pollInProgressSet) { + synchronized (pollLock) { + pollInProgress = false; + } } // cache wallet info last From 74e094fa992971c61e7fb0484b00cc68460f3fd7 Mon Sep 17 00:00:00 2001 From: woodser Date: Wed, 16 Oct 2024 07:33:43 -0400 Subject: [PATCH 28/46] poll trade wallet on error importing multisig or processing payout --- core/src/main/java/haveno/core/trade/Trade.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index dbab49cd..f63fcc96 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -1080,6 +1080,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { handleWalletError(e, sourceConnection); if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying + doPollWallet(); } } } @@ -1254,6 +1255,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { handleWalletError(e, sourceConnection); if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying + doPollWallet(); } } throw new RuntimeException("Failed to create payout tx for " + getClass().getSimpleName() + " " + getId()); @@ -1283,6 +1285,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { handleWalletError(e, sourceConnection); if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying + doPollWallet(); } finally { requestSaveWallet(); requestPersistence(); From 5c4fa7a53f7a66783d4f4a499fac740ef73b8a19 Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 25 Oct 2024 11:34:07 -0400 Subject: [PATCH 29/46] import multisig hex if needed on create payout tx --- core/src/main/java/haveno/core/trade/Trade.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index f63fcc96..72f84a50 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -1064,6 +1064,14 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { } } + public void importMultisigHexIfNeeded() { + synchronized (walletLock) { + if (wallet.isMultisigImportNeeded()) { + importMultisigHex(); + } + } + } + public void importMultisigHex() { synchronized (walletLock) { synchronized (HavenoUtils.getDaemonLock()) { // lock on daemon because import calls full refresh @@ -1184,6 +1192,11 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { // create payout tx synchronized (walletLock) { synchronized (HavenoUtils.getWalletFunctionLock()) { + + // import multisig hex if needed + importMultisigHexIfNeeded(); + + // create payout tx for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) { MoneroRpcConnection sourceConnection = xmrConnectionService.getConnection(); try { From 6db4812f0663f947deba11773638b7a730500b6c Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 25 Oct 2024 11:35:55 -0400 Subject: [PATCH 30/46] synchronize on lock to prepare payment sent or received --- .../tasks/BuyerPreparePaymentSentMessage.java | 23 +++++--- .../SellerPreparePaymentReceivedMessage.java | 53 +++++++++++-------- 2 files changed, 45 insertions(+), 31 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/BuyerPreparePaymentSentMessage.java b/core/src/main/java/haveno/core/trade/protocol/tasks/BuyerPreparePaymentSentMessage.java index 2cd4376c..8b739011 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/BuyerPreparePaymentSentMessage.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/BuyerPreparePaymentSentMessage.java @@ -36,6 +36,7 @@ package haveno.core.trade.protocol.tasks; import com.google.common.base.Preconditions; import haveno.common.taskrunner.TaskRunner; +import haveno.core.trade.HavenoUtils; import haveno.core.trade.Trade; import lombok.extern.slf4j.Slf4j; import monero.wallet.MoneroWallet; @@ -79,15 +80,21 @@ public class BuyerPreparePaymentSentMessage extends TradeTask { // create payout tx if we have seller's updated multisig hex if (trade.getSeller().getUpdatedMultisigHex() != null) { - // import multisig hex - trade.importMultisigHex(); + // synchronize on lock for wallet operations + synchronized (trade.getWalletLock()) { + synchronized (HavenoUtils.getWalletFunctionLock()) { - // create payout tx - log.info("Buyer creating unsigned payout tx for {} {} ", trade.getClass().getSimpleName(), trade.getShortId()); - MoneroTxWallet payoutTx = trade.createPayoutTx(); - trade.updatePayout(payoutTx); - trade.getSelf().setUnsignedPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex()); - trade.requestPersistence(); + // import multisig hex + trade.importMultisigHex(); + + // create payout tx + log.info("Buyer creating unsigned payout tx for {} {} ", trade.getClass().getSimpleName(), trade.getShortId()); + MoneroTxWallet payoutTx = trade.createPayoutTx(); + trade.updatePayout(payoutTx); + trade.getSelf().setUnsignedPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex()); + trade.requestPersistence(); + } + } } complete(); diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/SellerPreparePaymentReceivedMessage.java b/core/src/main/java/haveno/core/trade/protocol/tasks/SellerPreparePaymentReceivedMessage.java index a483026a..1452104e 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/SellerPreparePaymentReceivedMessage.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/SellerPreparePaymentReceivedMessage.java @@ -19,6 +19,7 @@ package haveno.core.trade.protocol.tasks; import haveno.common.taskrunner.TaskRunner; import haveno.core.support.dispute.Dispute; +import haveno.core.trade.HavenoUtils; import haveno.core.trade.Trade; import lombok.extern.slf4j.Slf4j; import monero.wallet.model.MoneroTxWallet; @@ -49,34 +50,40 @@ public class SellerPreparePaymentReceivedMessage extends TradeTask { trade.setPayoutTxHex(null); } - // import multisig hex unless already signed - if (trade.getPayoutTxHex() == null) { - trade.importMultisigHex(); - } + // synchronize on lock for wallet operations + synchronized (trade.getWalletLock()) { + synchronized (HavenoUtils.getWalletFunctionLock()) { - // verify, sign, and publish payout tx if given - if (trade.getBuyer().getPaymentSentMessage().getPayoutTxHex() != null) { - try { + // import multisig hex unless already signed if (trade.getPayoutTxHex() == null) { - log.info("Seller verifying, signing, and publishing payout tx for trade {}", trade.getId()); - trade.processPayoutTx(trade.getBuyer().getPaymentSentMessage().getPayoutTxHex(), true, true); - } else { - log.warn("Seller publishing previously signed payout tx for trade {}", trade.getId()); - trade.processPayoutTx(trade.getPayoutTxHex(), false, true); + trade.importMultisigHex(); + } + + // verify, sign, and publish payout tx if given + if (trade.getBuyer().getPaymentSentMessage().getPayoutTxHex() != null) { + try { + if (trade.getPayoutTxHex() == null) { + log.info("Seller verifying, signing, and publishing payout tx for trade {}", trade.getId()); + trade.processPayoutTx(trade.getBuyer().getPaymentSentMessage().getPayoutTxHex(), true, true); + } else { + log.warn("Seller publishing previously signed payout tx for trade {}", trade.getId()); + trade.processPayoutTx(trade.getPayoutTxHex(), false, true); + } + } catch (IllegalArgumentException | IllegalStateException e) { + log.warn("Illegal state or argument verifying, signing, and publishing payout tx for {} {}. Creating new unsigned payout tx. error={}. ", trade.getClass().getSimpleName(), trade.getId(), e.getMessage(), e); + createUnsignedPayoutTx(); + } catch (Exception e) { + log.warn("Error verifying, signing, and publishing payout tx for trade {}: {}", trade.getId(), e.getMessage(), e); + throw e; + } + } + + // otherwise create unsigned payout tx + else if (trade.getSelf().getUnsignedPayoutTxHex() == null) { + createUnsignedPayoutTx(); } - } catch (IllegalArgumentException | IllegalStateException e) { - log.warn("Illegal state or argument verifying, signing, and publishing payout tx for {} {}: {}. Creating new unsigned payout tx", trade.getClass().getSimpleName(), trade.getId(), e.getMessage(), e); - createUnsignedPayoutTx(); - } catch (Exception e) { - log.warn("Error verifying, signing, and publishing payout tx for trade {}: {}", trade.getId(), e.getMessage(), e); - throw e; } } - - // otherwise create unsigned payout tx - else if (trade.getSelf().getUnsignedPayoutTxHex() == null) { - createUnsignedPayoutTx(); - } } else if (trade.getArbitrator().getPaymentReceivedMessage().getSignedPayoutTxHex() != null && !trade.isPayoutPublished()) { // republish payout tx from previous message From cdb99a9cfb89ddd68b46544cffc7112b11800113 Mon Sep 17 00:00:00 2001 From: woodser Date: Thu, 7 Nov 2024 10:47:54 -0500 Subject: [PATCH 31/46] support opening dispute with corrupt trade wallet --- .../haveno/core/support/dispute/DisputeManager.java | 8 +++++++- .../pendingtrades/PendingTradesDataModel.java | 11 +++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/haveno/core/support/dispute/DisputeManager.java b/core/src/main/java/haveno/core/support/dispute/DisputeManager.java index eb49e0c5..6fca89f8 100644 --- a/core/src/main/java/haveno/core/support/dispute/DisputeManager.java +++ b/core/src/main/java/haveno/core/support/dispute/DisputeManager.java @@ -393,8 +393,14 @@ public abstract class DisputeManager> extends Sup chatMessage.setSystemMessage(true); dispute.addAndPersistChatMessage(chatMessage); + // export latest multisig hex + try { + trade.exportMultisigHex(); + } catch (Exception e) { + log.error("Failed to export multisig hex", e); + } + // create dispute opened message - trade.exportMultisigHex(); NodeAddress agentNodeAddress = getAgentNodeAddress(dispute); DisputeOpenedMessage disputeOpenedMessage = new DisputeOpenedMessage(dispute, p2PService.getAddress(), diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java index 944c977b..81191669 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java @@ -548,10 +548,17 @@ public class PendingTradesDataModel extends ActivatableDataModel { sendDisputeOpenedMessage(dispute, disputeManager); tradeManager.requestPersistence(); } else if (useArbitration) { - // Only if we have completed mediation we allow arbitration disputeManager = arbitrationManager; Dispute dispute = disputesService.createDisputeForTrade(trade, offer, pubKeyRingProvider.get(), isMaker, isSupportTicket); - trade.exportMultisigHex(); + + // export latest multisig hex + try { + trade.exportMultisigHex(); + } catch (Exception e) { + log.error("Failed to export multisig hex", e); + } + + // send dispute opened message sendDisputeOpenedMessage(dispute, disputeManager); tradeManager.requestPersistence(); } else { From 1af87b2db97002aeb6b6b2e9f1980bd84a78fba5 Mon Sep 17 00:00:00 2001 From: woodser Date: Thu, 7 Nov 2024 10:44:08 -0500 Subject: [PATCH 32/46] do not initialize failed trades until moved back to pending trades --- .../main/java/haveno/core/trade/TradeManager.java | 13 ++++++++++++- .../portfolio/pendingtrades/PendingTradesView.java | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/TradeManager.java b/core/src/main/java/haveno/core/trade/TradeManager.java index 10db542e..dc0427c4 100644 --- a/core/src/main/java/haveno/core/trade/TradeManager.java +++ b/core/src/main/java/haveno/core/trade/TradeManager.java @@ -450,6 +450,13 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi return; } + // skip if marked as failed + if (failedTradesManager.getObservableList().contains(trade)) { + log.warn("Skipping initialization of failed trade {} {}", trade.getClass().getSimpleName(), trade.getId()); + tradesToSkip.add(trade); + return; + } + // initialize trade initPersistedTrade(trade); @@ -1060,7 +1067,11 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi private void addTradeToPendingTrades(Trade trade) { if (!trade.isInitialized()) { - initPersistedTrade(trade); + try { + initPersistedTrade(trade); + } catch (Exception e) { + log.warn("Error initializing {} {} on move to pending trades", trade.getClass().getSimpleName(), trade.getShortId(), e); + } } addTrade(trade); } diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesView.java b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesView.java index 5157c9b4..73160484 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesView.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/PendingTradesView.java @@ -404,7 +404,7 @@ public class PendingTradesView extends ActivatableViewAndModel Date: Thu, 7 Nov 2024 11:56:56 -0500 Subject: [PATCH 33/46] fix bug restoring wrong wallet cache --- .../haveno/core/xmr/wallet/XmrWalletService.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index 6ffd6537..edf6e269 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -1482,18 +1482,18 @@ public class XmrWalletService extends XmrWalletBase { try { // rename wallet cache to backup - String cachePath = walletDir.toString() + File.separator + MONERO_WALLET_NAME; + String cachePath = walletDir.toString() + File.separator + getWalletName(config.getPath()); File originalCacheFile = new File(cachePath); if (originalCacheFile.exists()) originalCacheFile.renameTo(new File(cachePath + ".backup")); // copy latest wallet cache backup to main folder - File backupCacheFile = FileUtil.getLatestBackupFile(walletDir, MONERO_WALLET_NAME); + File backupCacheFile = FileUtil.getLatestBackupFile(walletDir, getWalletName(config.getPath())); if (backupCacheFile != null) FileUtil.copyFile(backupCacheFile, new File(cachePath)); // retry opening wallet without original cache try { walletFull = MoneroWalletFull.openWallet(config); - log.info("Successfully opened full wallet using backup cache"); + log.warn("Successfully opened full wallet using backup cache"); retrySuccessful = true; } catch (Exception e2) { // ignore @@ -1587,18 +1587,18 @@ public class XmrWalletService extends XmrWalletBase { try { // rename wallet cache to backup - String cachePath = walletDir.toString() + File.separator + MONERO_WALLET_NAME; + String cachePath = walletDir.toString() + File.separator + config.getPath(); File originalCacheFile = new File(cachePath); if (originalCacheFile.exists()) originalCacheFile.renameTo(new File(cachePath + ".backup")); // copy latest wallet cache backup to main folder - File backupCacheFile = FileUtil.getLatestBackupFile(walletDir, MONERO_WALLET_NAME); + File backupCacheFile = FileUtil.getLatestBackupFile(walletDir, config.getPath()); if (backupCacheFile != null) FileUtil.copyFile(backupCacheFile, new File(cachePath)); // retry opening wallet without original cache try { walletRpc.openWallet(config); - log.info("Successfully opened RPC wallet using backup cache"); + log.warn("Successfully opened RPC wallet using backup cache"); retrySuccessful = true; } catch (Exception e2) { // ignore From 8a9b4ffe11c4d1fea18b7653d63347dd6e51b612 Mon Sep 17 00:00:00 2001 From: woodser Date: Thu, 7 Nov 2024 11:53:15 -0500 Subject: [PATCH 34/46] backup trade wallet on successful save and close --- core/src/main/java/haveno/core/trade/Trade.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index 72f84a50..e7b7584e 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -937,6 +937,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { if (wallet == null) throw new RuntimeException("Trade wallet to close is not open for trade " + getId()); stopPolling(); xmrWalletService.closeWallet(wallet, true); + maybeBackupWallet(); wallet = null; pollPeriodMs = null; } @@ -1561,9 +1562,6 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { forceCloseWallet(); } - // backup trade wallet if applicable - maybeBackupWallet(); - // de-initialize if (idlePayoutSyncer != null) { xmrWalletService.removeWalletListener(idlePayoutSyncer); From b348a81f13cf37da022fae2a3394428e64ea4630 Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 8 Nov 2024 06:20:32 -0500 Subject: [PATCH 35/46] increase number of wallet backups to 2 --- core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index edf6e269..2e67e5ba 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -121,7 +121,7 @@ public class XmrWalletService extends XmrWalletBase { private static final String MONERO_WALLET_NAME = "haveno_XMR"; private static final String KEYS_FILE_POSTFIX = ".keys"; private static final String ADDRESS_FILE_POSTFIX = ".address.txt"; - private static final int NUM_MAX_WALLET_BACKUPS = 1; + private static final int NUM_MAX_WALLET_BACKUPS = 2; private static final int MAX_SYNC_ATTEMPTS = 3; private static final boolean PRINT_RPC_STACK_TRACE = false; private static final String THREAD_ID = XmrWalletService.class.getSimpleName(); From 577cfa249e88d313bec080bc87180a1840a24172 Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 8 Nov 2024 08:22:16 -0500 Subject: [PATCH 36/46] try opening wallets with all backup cache files then no cache file --- .../java/haveno/common/file/FileUtil.java | 13 +- .../core/xmr/wallet/XmrWalletService.java | 118 ++++++++++++------ 2 files changed, 91 insertions(+), 40 deletions(-) diff --git a/common/src/main/java/haveno/common/file/FileUtil.java b/common/src/main/java/haveno/common/file/FileUtil.java index 27058f30..8bb6ae75 100644 --- a/common/src/main/java/haveno/common/file/FileUtil.java +++ b/common/src/main/java/haveno/common/file/FileUtil.java @@ -74,7 +74,7 @@ public class FileUtil { } } - public static File getLatestBackupFile(File dir, String fileName) { + public static List getBackupFiles(File dir, String fileName) { File backupDir = new File(Paths.get(dir.getAbsolutePath(), BACKUP_DIR).toString()); if (!backupDir.exists()) return null; String dirName = "backups_" + fileName; @@ -82,9 +82,14 @@ public class FileUtil { File backupFileDir = new File(Paths.get(backupDir.getAbsolutePath(), dirName).toString()); if (!backupFileDir.exists()) return null; File[] files = backupFileDir.listFiles(); - if (files == null || files.length == 0) return null; - Arrays.sort(files, Comparator.comparing(File::getName)); - return files[files.length - 1]; + return Arrays.asList(files); + } + + public static File getLatestBackupFile(File dir, String fileName) { + List files = getBackupFiles(dir, fileName); + if (files.isEmpty()) return null; + files.sort(Comparator.comparing(File::getName)); + return files.get(files.size() - 1); } public static void deleteRollingBackup(File dir, String fileName) { diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index 2e67e5ba..68577665 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -1477,7 +1477,7 @@ public class XmrWalletService extends XmrWalletBase { try { walletFull = MoneroWalletFull.openWallet(config); } catch (Exception e) { - log.warn("Failed to open full wallet '{}', attempting to use backup cache, error={}", config.getPath(), e.getMessage()); + log.warn("Failed to open full wallet '{}', attempting to use backup cache files, error={}", config.getPath(), e.getMessage()); boolean retrySuccessful = false; try { @@ -1486,17 +1486,24 @@ public class XmrWalletService extends XmrWalletBase { File originalCacheFile = new File(cachePath); if (originalCacheFile.exists()) originalCacheFile.renameTo(new File(cachePath + ".backup")); - // copy latest wallet cache backup to main folder - File backupCacheFile = FileUtil.getLatestBackupFile(walletDir, getWalletName(config.getPath())); - if (backupCacheFile != null) FileUtil.copyFile(backupCacheFile, new File(cachePath)); + // try opening wallet with backup cache files in descending order + List backupCacheFiles = FileUtil.getBackupFiles(walletDir, getWalletName(config.getPath())); + Collections.reverse(backupCacheFiles); + for (File backupCacheFile : backupCacheFiles) { + try { + FileUtil.copyFile(backupCacheFile, new File(cachePath)); + walletFull = MoneroWalletFull.openWallet(config); + log.warn("Successfully opened full wallet using backup cache"); + retrySuccessful = true; + break; + } catch (Exception e2) { - // retry opening wallet without original cache - try { - walletFull = MoneroWalletFull.openWallet(config); - log.warn("Successfully opened full wallet using backup cache"); - retrySuccessful = true; - } catch (Exception e2) { - // ignore + // delete cache file if failed to open + File cacheFile = new File(cachePath); + if (cacheFile.exists()) cacheFile.delete(); + File unportableCacheFile = new File(cachePath + ".unportable"); + if (unportableCacheFile.exists()) unportableCacheFile.delete(); + } } // handle success or failure @@ -1505,14 +1512,30 @@ public class XmrWalletService extends XmrWalletBase { if (originalCacheBackup.exists()) originalCacheBackup.delete(); // delete original wallet cache backup } else { - // restore original wallet cache - log.warn("Failed to open full wallet using backup cache, restoring original cache"); - File cacheFile = new File(cachePath); - if (cacheFile.exists()) cacheFile.delete(); - if (originalCacheBackup.exists()) originalCacheBackup.renameTo(new File(cachePath)); + // retry opening wallet after cache deleted + try { + log.warn("Failed to open full wallet using backup cache files, retrying with cache deleted"); + walletFull = MoneroWalletFull.openWallet(config); + log.warn("Successfully opened full wallet after cache deleted"); + retrySuccessful = true; + } catch (Exception e2) { + // ignore + } - // throw exception - throw e; + // handle success or failure + if (retrySuccessful) { + if (originalCacheBackup.exists()) originalCacheBackup.delete(); // delete original wallet cache backup + } else { + + // restore original wallet cache + log.warn("Failed to open full wallet after deleting cache, restoring original cache"); + File cacheFile = new File(cachePath); + if (cacheFile.exists()) cacheFile.delete(); + if (originalCacheBackup.exists()) originalCacheBackup.renameTo(new File(cachePath)); + + // throw original exception + throw e; + } } } catch (Exception e2) { throw e; // throw original exception @@ -1582,7 +1605,7 @@ public class XmrWalletService extends XmrWalletBase { try { walletRpc.openWallet(config); } catch (Exception e) { - log.warn("Failed to open RPC wallet '{}', attempting to use backup cache, error={}", config.getPath(), e.getMessage()); + log.warn("Failed to open RPC wallet '{}', attempting to use backup cache files, error={}", config.getPath(), e.getMessage()); boolean retrySuccessful = false; try { @@ -1591,17 +1614,24 @@ public class XmrWalletService extends XmrWalletBase { File originalCacheFile = new File(cachePath); if (originalCacheFile.exists()) originalCacheFile.renameTo(new File(cachePath + ".backup")); - // copy latest wallet cache backup to main folder - File backupCacheFile = FileUtil.getLatestBackupFile(walletDir, config.getPath()); - if (backupCacheFile != null) FileUtil.copyFile(backupCacheFile, new File(cachePath)); + // try opening wallet with backup cache files in descending order + List backupCacheFiles = FileUtil.getBackupFiles(walletDir, config.getPath()); + Collections.reverse(backupCacheFiles); + for (File backupCacheFile : backupCacheFiles) { + try { + FileUtil.copyFile(backupCacheFile, new File(cachePath)); + walletRpc.openWallet(config); + log.warn("Successfully opened RPC wallet using backup cache"); + retrySuccessful = true; + break; + } catch (Exception e2) { - // retry opening wallet without original cache - try { - walletRpc.openWallet(config); - log.warn("Successfully opened RPC wallet using backup cache"); - retrySuccessful = true; - } catch (Exception e2) { - // ignore + // delete cache file if failed to open + File cacheFile = new File(cachePath); + if (cacheFile.exists()) cacheFile.delete(); + File unportableCacheFile = new File(cachePath + ".unportable"); + if (unportableCacheFile.exists()) unportableCacheFile.delete(); + } } // handle success or failure @@ -1610,14 +1640,30 @@ public class XmrWalletService extends XmrWalletBase { if (originalCacheBackup.exists()) originalCacheBackup.delete(); // delete original wallet cache backup } else { - // restore original wallet cache - log.warn("Failed to open RPC wallet using backup cache, restoring original cache"); - File cacheFile = new File(cachePath); - if (cacheFile.exists()) cacheFile.delete(); - if (originalCacheBackup.exists()) originalCacheBackup.renameTo(new File(cachePath)); + // retry opening wallet after cache deleted + try { + log.warn("Failed to open RPC wallet using backup cache files, retrying with cache deleted"); + walletRpc.openWallet(config); + log.warn("Successfully opened RPC wallet after cache deleted"); + retrySuccessful = true; + } catch (Exception e2) { + // ignore + } - // throw exception - throw e; + // handle success or failure + if (retrySuccessful) { + if (originalCacheBackup.exists()) originalCacheBackup.delete(); // delete original wallet cache backup + } else { + + // restore original wallet cache + log.warn("Failed to open RPC wallet after deleting cache, restoring original cache"); + File cacheFile = new File(cachePath); + if (cacheFile.exists()) cacheFile.delete(); + if (originalCacheBackup.exists()) originalCacheBackup.renameTo(new File(cachePath)); + + // throw original exception + throw e; + } } } catch (Exception e2) { throw e; // throw original exception From 0d82f8827fb3e19614c0c82bd10f2327f7f074c5 Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 8 Nov 2024 08:14:16 -0500 Subject: [PATCH 37/46] dispute is pending until requested and chat disabled until acked --- core/src/main/java/haveno/core/support/dispute/Dispute.java | 4 ++++ .../main/java/haveno/core/support/dispute/DisputeSession.java | 2 +- .../java/haveno/desktop/main/support/dispute/DisputeView.java | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/haveno/core/support/dispute/Dispute.java b/core/src/main/java/haveno/core/support/dispute/Dispute.java index 5828710d..268cb3e2 100644 --- a/core/src/main/java/haveno/core/support/dispute/Dispute.java +++ b/core/src/main/java/haveno/core/support/dispute/Dispute.java @@ -466,6 +466,10 @@ public final class Dispute implements NetworkPayload, PersistablePayload { return this.disputeState == State.NEW; } + public boolean isOpen() { + return this.disputeState == State.OPEN || this.disputeState == State.REOPENED; + } + public boolean isClosed() { return this.disputeState == State.CLOSED; } diff --git a/core/src/main/java/haveno/core/support/dispute/DisputeSession.java b/core/src/main/java/haveno/core/support/dispute/DisputeSession.java index 3b1b80e5..5507fecc 100644 --- a/core/src/main/java/haveno/core/support/dispute/DisputeSession.java +++ b/core/src/main/java/haveno/core/support/dispute/DisputeSession.java @@ -70,7 +70,7 @@ public abstract class DisputeSession extends SupportSession { @Override public boolean chatIsOpen() { - return dispute != null && !dispute.isClosed(); + return dispute != null && dispute.isOpen(); } @Override diff --git a/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java b/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java index f62761d4..14e85b78 100644 --- a/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java +++ b/desktop/src/main/java/haveno/desktop/main/support/dispute/DisputeView.java @@ -1420,6 +1420,8 @@ public abstract class DisputeView extends ActivatableView implements } if (dispute.isClosed()) return Res.get("support.closed"); switch (trade.getDisputeState()) { + case NO_DISPUTE: + return Res.get("shared.pending"); case DISPUTE_REQUESTED: return Res.get("support.requested"); default: From 8f5e56b9dc6b73f7f25bdb4d4e74c88d880e4e8c Mon Sep 17 00:00:00 2001 From: woodser Date: Sun, 10 Nov 2024 11:15:59 -0500 Subject: [PATCH 38/46] prefer local price provider, preserve provider when banned nodes applied --- .../core/provider/ProvidersRepository.java | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/haveno/core/provider/ProvidersRepository.java b/core/src/main/java/haveno/core/provider/ProvidersRepository.java index 2a657bc8..a31794a0 100644 --- a/core/src/main/java/haveno/core/provider/ProvidersRepository.java +++ b/core/src/main/java/haveno/core/provider/ProvidersRepository.java @@ -37,6 +37,7 @@ package haveno.core.provider; import com.google.inject.Inject; import com.google.inject.name.Named; import haveno.common.config.Config; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -47,6 +48,8 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public class ProvidersRepository { + + private static final String DEFAULT_LOCAL_NODE = "http://localhost:8078/"; private static final List DEFAULT_NODES = Arrays.asList( "http://elaxlgigphpicy5q7pi5wkz2ko2vgjbq4576vic7febmx4xcxvk6deqd.onion/", // Haveno "http://lrrgpezvdrbpoqvkavzobmj7dr2otxc5x6wgktrw337bk6mxsvfp5yid.onion/" // Cake @@ -78,19 +81,22 @@ public class ProvidersRepository { this.providersFromProgramArgs = providers; this.useLocalhostForP2P = useLocalhostForP2P; - Collections.shuffle(DEFAULT_NODES); + Collections.shuffle(DEFAULT_NODES); // randomize order of default nodes applyBannedNodes(config.bannedPriceRelayNodes); } public void applyBannedNodes(@Nullable List bannedNodes) { this.bannedNodes = bannedNodes; + + // fill provider list fillProviderList(); - selectNextProviderBaseUrl(); + + // select next provider if current provider is null or banned + if (baseUrl == null || isBanned(baseUrl)) selectNextProviderBaseUrl(); if (bannedNodes != null && !bannedNodes.isEmpty()) { - log.info("Excluded provider nodes from filter: nodes={}, selected provider baseUrl={}, providerList={}", - bannedNodes, baseUrl, providerList); + log.info("Excluded provider nodes from filter: nodes={}, selected provider baseUrl={}, providerList={}", bannedNodes, baseUrl, providerList); } } @@ -129,22 +135,30 @@ public class ProvidersRepository { // If we run in localhost mode we don't have the tor node running, so we need a clearnet host // Use localhost for using a locally running provider providers = List.of( - "http://localhost:8078/", + DEFAULT_LOCAL_NODE, "https://price.haveno.network/", "http://173.230.142.36:8078/"); } else { - providers = DEFAULT_NODES; + providers = new ArrayList(); + providers.add(DEFAULT_LOCAL_NODE); // try local provider first + providers.addAll(DEFAULT_NODES); } } else { providers = providersFromProgramArgs; } providerList = providers.stream() - .filter(e -> bannedNodes == null || - !bannedNodes.contains(e.replace("http://", "") - .replace("/", "") - .replace(".onion", ""))) + .filter(e -> !isBanned(e)) .map(e -> e.endsWith("/") ? e : e + "/") .map(e -> e.startsWith("http") ? e : "http://" + e) .collect(Collectors.toList()); } + + private boolean isBanned(String provider) { + if (bannedNodes == null) return false; + return bannedNodes.stream() + .anyMatch(e -> provider.replace("http://", "") + .replace("/", "") + .replace(".onion", "") + .equals(e)); + } } From 0450900b377431c6f34f6333a93c5ed2772e708e Mon Sep 17 00:00:00 2001 From: woodser Date: Wed, 16 Oct 2024 15:19:34 -0400 Subject: [PATCH 39/46] validate pending offer --- .../main/java/haveno/core/offer/Offer.java | 2 +- .../haveno/core/offer/OpenOfferManager.java | 9 ++ .../offer/placeoffer/tasks/ValidateOffer.java | 119 +++++++++--------- 3 files changed, 73 insertions(+), 57 deletions(-) diff --git a/core/src/main/java/haveno/core/offer/Offer.java b/core/src/main/java/haveno/core/offer/Offer.java index 675fbeb5..b3c81c5b 100644 --- a/core/src/main/java/haveno/core/offer/Offer.java +++ b/core/src/main/java/haveno/core/offer/Offer.java @@ -200,7 +200,7 @@ public class Offer implements NetworkPayload, PersistablePayload { return null; } } else { - log.trace("We don't have a market price. " + + log.warn("We don't have a market price. " + "That case could only happen if you don't have a price feed."); return null; } diff --git a/core/src/main/java/haveno/core/offer/OpenOfferManager.java b/core/src/main/java/haveno/core/offer/OpenOfferManager.java index a5c3194c..af898459 100644 --- a/core/src/main/java/haveno/core/offer/OpenOfferManager.java +++ b/core/src/main/java/haveno/core/offer/OpenOfferManager.java @@ -62,6 +62,7 @@ import haveno.core.offer.messages.SignOfferRequest; import haveno.core.offer.messages.SignOfferResponse; import haveno.core.offer.placeoffer.PlaceOfferModel; import haveno.core.offer.placeoffer.PlaceOfferProtocol; +import haveno.core.offer.placeoffer.tasks.ValidateOffer; import haveno.core.provider.price.PriceFeedService; import haveno.core.support.dispute.arbitration.arbitrator.Arbitrator; import haveno.core.support.dispute.arbitration.arbitrator.ArbitratorManager; @@ -934,6 +935,14 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe return; } + // validate offer + try { + ValidateOffer.validateOffer(openOffer.getOffer(), accountAgeWitnessService, user); + } catch (Exception e) { + errorMessageHandler.handleErrorMessage("Failed to validate offer: " + e.getMessage()); + return; + } + // cancel offer if scheduled txs unavailable if (openOffer.getScheduledTxHashes() != null) { boolean scheduledTxsAvailable = true; diff --git a/core/src/main/java/haveno/core/offer/placeoffer/tasks/ValidateOffer.java b/core/src/main/java/haveno/core/offer/placeoffer/tasks/ValidateOffer.java index 5f683bac..dfb1b2fa 100644 --- a/core/src/main/java/haveno/core/offer/placeoffer/tasks/ValidateOffer.java +++ b/core/src/main/java/haveno/core/offer/placeoffer/tasks/ValidateOffer.java @@ -19,10 +19,12 @@ package haveno.core.offer.placeoffer.tasks; import haveno.common.taskrunner.Task; import haveno.common.taskrunner.TaskRunner; +import haveno.core.account.witness.AccountAgeWitnessService; import haveno.core.offer.Offer; import haveno.core.offer.placeoffer.PlaceOfferModel; import haveno.core.trade.HavenoUtils; import haveno.core.trade.messages.TradeMessage; +import haveno.core.user.User; import org.bitcoinj.core.Coin; import java.math.BigInteger; @@ -41,55 +43,7 @@ public class ValidateOffer extends Task { try { runInterceptHook(); - // Coins - checkBINotNullOrZero(offer.getAmount(), "Amount"); - checkBINotNullOrZero(offer.getMinAmount(), "MinAmount"); - //checkCoinNotNullOrZero(offer.getTxFee(), "txFee"); // TODO: remove from data model - checkBINotNullOrZero(offer.getMaxTradeLimit(), "MaxTradeLimit"); - if (offer.getMakerFeePct() < 0) throw new IllegalArgumentException("Maker fee must be >= 0% but was " + offer.getMakerFeePct()); - if (offer.getTakerFeePct() < 0) throw new IllegalArgumentException("Taker fee must be >= 0% but was " + offer.getTakerFeePct()); - if (offer.getBuyerSecurityDepositPct() <= 0) throw new IllegalArgumentException("Buyer security deposit percent must be positive but was " + offer.getBuyerSecurityDepositPct()); - if (offer.getSellerSecurityDepositPct() <= 0) throw new IllegalArgumentException("Seller security deposit percent must be positive but was " + offer.getSellerSecurityDepositPct()); - - // We remove those checks to be more flexible with future changes. - /*checkArgument(offer.getMakerFee().value >= FeeService.getMinMakerFee(offer.isCurrencyForMakerFeeBtc()).value, - "createOfferFee must not be less than FeeService.MIN_CREATE_OFFER_FEE_IN_BTC. " + - "MakerFee=" + offer.getMakerFee().toFriendlyString());*/ - /*checkArgument(offer.getBuyerSecurityDeposit().value >= ProposalConsensus.getMinBuyerSecurityDeposit().value, - "buyerSecurityDeposit must not be less than ProposalConsensus.MIN_BUYER_SECURITY_DEPOSIT. " + - "buyerSecurityDeposit=" + offer.getBuyerSecurityDeposit().toFriendlyString()); - checkArgument(offer.getBuyerSecurityDeposit().value <= ProposalConsensus.getMaxBuyerSecurityDeposit().value, - "buyerSecurityDeposit must not be larger than ProposalConsensus.MAX_BUYER_SECURITY_DEPOSIT. " + - "buyerSecurityDeposit=" + offer.getBuyerSecurityDeposit().toFriendlyString()); - checkArgument(offer.getSellerSecurityDeposit().value == ProposalConsensus.getSellerSecurityDeposit().value, - "sellerSecurityDeposit must be equal to ProposalConsensus.SELLER_SECURITY_DEPOSIT. " + - "sellerSecurityDeposit=" + offer.getSellerSecurityDeposit().toFriendlyString());*/ - /*checkArgument(offer.getMinAmount().compareTo(ProposalConsensus.getMinTradeAmount()) >= 0, - "MinAmount is less than " + ProposalConsensus.getMinTradeAmount().toFriendlyString());*/ - - long maxAmount = model.getAccountAgeWitnessService().getMyTradeLimit(model.getUser().getPaymentAccount(offer.getMakerPaymentAccountId()), offer.getCurrencyCode(), offer.getDirection()); - checkArgument(offer.getAmount().longValueExact() <= maxAmount, - "Amount is larger than " + HavenoUtils.atomicUnitsToXmr(offer.getPaymentMethod().getMaxTradeLimit(offer.getCurrencyCode())) + " XMR"); - checkArgument(offer.getAmount().compareTo(offer.getMinAmount()) >= 0, "MinAmount is larger than Amount"); - - checkNotNull(offer.getPrice(), "Price is null"); - if (!offer.isUseMarketBasedPrice()) checkArgument(offer.getPrice().isPositive(), - "Price must be positive unless using market based price. price=" + offer.getPrice().toFriendlyString()); - - checkArgument(offer.getDate().getTime() > 0, - "Date must not be 0. date=" + offer.getDate().toString()); - - checkNotNull(offer.getCurrencyCode(), "Currency is null"); - checkNotNull(offer.getDirection(), "Direction is null"); - checkNotNull(offer.getId(), "Id is null"); - checkNotNull(offer.getPubKeyRing(), "pubKeyRing is null"); - checkNotNull(offer.getMinAmount(), "MinAmount is null"); - checkNotNull(offer.getPrice(), "Price is null"); - checkNotNull(offer.getVersionNr(), "VersionNr is null"); - checkArgument(offer.getMaxTradePeriod() > 0, - "maxTradePeriod must be positive. maxTradePeriod=" + offer.getMaxTradePeriod()); - // TODO check upper and lower bounds for fiat - // TODO check rest of new parameters + validateOffer(offer, model.getAccountAgeWitnessService(), model.getUser()); complete(); } catch (Exception e) { @@ -100,42 +54,95 @@ public class ValidateOffer extends Task { } } - public static void checkBINotNullOrZero(BigInteger value, String name) { + public static void validateOffer(Offer offer, AccountAgeWitnessService accountAgeWitnessService, User user) { + + // Coins + checkBINotNullOrZero(offer.getAmount(), "Amount"); + checkBINotNullOrZero(offer.getMinAmount(), "MinAmount"); + //checkCoinNotNullOrZero(offer.getTxFee(), "txFee"); // TODO: remove from data model + checkBINotNullOrZero(offer.getMaxTradeLimit(), "MaxTradeLimit"); + if (offer.getMakerFeePct() < 0) throw new IllegalArgumentException("Maker fee must be >= 0% but was " + offer.getMakerFeePct()); + if (offer.getTakerFeePct() < 0) throw new IllegalArgumentException("Taker fee must be >= 0% but was " + offer.getTakerFeePct()); + if (offer.getBuyerSecurityDepositPct() <= 0) throw new IllegalArgumentException("Buyer security deposit percent must be positive but was " + offer.getBuyerSecurityDepositPct()); + if (offer.getSellerSecurityDepositPct() <= 0) throw new IllegalArgumentException("Seller security deposit percent must be positive but was " + offer.getSellerSecurityDepositPct()); + + // We remove those checks to be more flexible with future changes. + /*checkArgument(offer.getMakerFee().value >= FeeService.getMinMakerFee(offer.isCurrencyForMakerFeeBtc()).value, + "createOfferFee must not be less than FeeService.MIN_CREATE_OFFER_FEE_IN_BTC. " + + "MakerFee=" + offer.getMakerFee().toFriendlyString());*/ + /*checkArgument(offer.getBuyerSecurityDeposit().value >= ProposalConsensus.getMinBuyerSecurityDeposit().value, + "buyerSecurityDeposit must not be less than ProposalConsensus.MIN_BUYER_SECURITY_DEPOSIT. " + + "buyerSecurityDeposit=" + offer.getBuyerSecurityDeposit().toFriendlyString()); + checkArgument(offer.getBuyerSecurityDeposit().value <= ProposalConsensus.getMaxBuyerSecurityDeposit().value, + "buyerSecurityDeposit must not be larger than ProposalConsensus.MAX_BUYER_SECURITY_DEPOSIT. " + + "buyerSecurityDeposit=" + offer.getBuyerSecurityDeposit().toFriendlyString()); + checkArgument(offer.getSellerSecurityDeposit().value == ProposalConsensus.getSellerSecurityDeposit().value, + "sellerSecurityDeposit must be equal to ProposalConsensus.SELLER_SECURITY_DEPOSIT. " + + "sellerSecurityDeposit=" + offer.getSellerSecurityDeposit().toFriendlyString());*/ + /*checkArgument(offer.getMinAmount().compareTo(ProposalConsensus.getMinTradeAmount()) >= 0, + "MinAmount is less than " + ProposalConsensus.getMinTradeAmount().toFriendlyString());*/ + + long maxAmount = accountAgeWitnessService.getMyTradeLimit(user.getPaymentAccount(offer.getMakerPaymentAccountId()), offer.getCurrencyCode(), offer.getDirection()); + checkArgument(offer.getAmount().longValueExact() <= maxAmount, + "Amount is larger than " + HavenoUtils.atomicUnitsToXmr(offer.getPaymentMethod().getMaxTradeLimit(offer.getCurrencyCode())) + " XMR"); + checkArgument(offer.getAmount().compareTo(offer.getMinAmount()) >= 0, "MinAmount is larger than Amount"); + + checkNotNull(offer.getPrice(), "Price is null"); + if (!offer.isUseMarketBasedPrice()) checkArgument(offer.getPrice().isPositive(), + "Price must be positive unless using market based price. price=" + offer.getPrice().toFriendlyString()); + + checkArgument(offer.getDate().getTime() > 0, + "Date must not be 0. date=" + offer.getDate().toString()); + + checkNotNull(offer.getCurrencyCode(), "Currency is null"); + checkNotNull(offer.getDirection(), "Direction is null"); + checkNotNull(offer.getId(), "Id is null"); + checkNotNull(offer.getPubKeyRing(), "pubKeyRing is null"); + checkNotNull(offer.getMinAmount(), "MinAmount is null"); + checkNotNull(offer.getPrice(), "Price is null"); + checkNotNull(offer.getVersionNr(), "VersionNr is null"); + checkArgument(offer.getMaxTradePeriod() > 0, + "maxTradePeriod must be positive. maxTradePeriod=" + offer.getMaxTradePeriod()); + // TODO check upper and lower bounds for fiat + // TODO check rest of new parameters + } + + private static void checkBINotNullOrZero(BigInteger value, String name) { checkNotNull(value, name + " is null"); checkArgument(value.compareTo(BigInteger.ZERO) > 0, name + " must be positive. " + name + "=" + value); } - public static void checkCoinNotNullOrZero(Coin value, String name) { + private static void checkCoinNotNullOrZero(Coin value, String name) { checkNotNull(value, name + " is null"); checkArgument(value.isPositive(), name + " must be positive. " + name + "=" + value.toFriendlyString()); } - public static String nonEmptyStringOf(String value) { + private static String nonEmptyStringOf(String value) { checkNotNull(value); checkArgument(value.length() > 0); return value; } - public static long nonNegativeLongOf(long value) { + private static long nonNegativeLongOf(long value) { checkArgument(value >= 0); return value; } - public static Coin nonZeroCoinOf(Coin value) { + private static Coin nonZeroCoinOf(Coin value) { checkNotNull(value); checkArgument(!value.isZero()); return value; } - public static Coin positiveCoinOf(Coin value) { + private static Coin positiveCoinOf(Coin value) { checkNotNull(value); checkArgument(value.isPositive()); return value; } - public static void checkTradeId(String tradeId, TradeMessage tradeMessage) { + private static void checkTradeId(String tradeId, TradeMessage tradeMessage) { checkArgument(tradeId.equals(tradeMessage.getOfferId())); } } From 2c3cd5208b7ffa7bb84e3980442233fcd4dc38b2 Mon Sep 17 00:00:00 2001 From: woodser Date: Sun, 10 Nov 2024 11:22:10 -0500 Subject: [PATCH 40/46] support USDT-ERC20 and USDT-TRC20 --- .../main/java/haveno/asset/Trc20Token.java | 29 +++++++++++++ .../haveno/asset/tokens/TetherUSDERC20.java | 11 +++++ .../haveno/asset/tokens/TetherUSDTRC20.java | 11 +++++ .../META-INF/services/haveno.asset.Asset | 2 + .../asset/coins/TetherUSDERC20Test.java | 43 +++++++++++++++++++ .../asset/coins/TetherUSDTRC20Test.java | 42 ++++++++++++++++++ .../java/haveno/core/locale/CurrencyUtil.java | 18 ++++++++ .../core/provider/price/PriceFeedService.java | 7 +-- .../core/provider/price/PriceProvider.java | 2 + .../main/offer/MutableOfferViewModel.java | 2 +- .../presentation/MarketPricePresentation.java | 16 +++++-- 11 files changed, 176 insertions(+), 7 deletions(-) create mode 100644 assets/src/main/java/haveno/asset/Trc20Token.java create mode 100644 assets/src/main/java/haveno/asset/tokens/TetherUSDERC20.java create mode 100644 assets/src/main/java/haveno/asset/tokens/TetherUSDTRC20.java create mode 100644 assets/src/test/java/haveno/asset/coins/TetherUSDERC20Test.java create mode 100644 assets/src/test/java/haveno/asset/coins/TetherUSDTRC20Test.java diff --git a/assets/src/main/java/haveno/asset/Trc20Token.java b/assets/src/main/java/haveno/asset/Trc20Token.java new file mode 100644 index 00000000..3cffa344 --- /dev/null +++ b/assets/src/main/java/haveno/asset/Trc20Token.java @@ -0,0 +1,29 @@ +/* + * This file is part of Haveno. + * + * Haveno is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Haveno is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Haveno. If not, see . + */ + +package haveno.asset; + +/** + * Abstract base class for Tron-based {@link Token}s that implement the + * TRC-20 Token Standard. + */ +public abstract class Trc20Token extends Token { + + public Trc20Token(String name, String tickerSymbol) { + super(name, tickerSymbol, new RegexAddressValidator("T[A-Za-z1-9]{33}")); + } +} diff --git a/assets/src/main/java/haveno/asset/tokens/TetherUSDERC20.java b/assets/src/main/java/haveno/asset/tokens/TetherUSDERC20.java new file mode 100644 index 00000000..1afb7ff1 --- /dev/null +++ b/assets/src/main/java/haveno/asset/tokens/TetherUSDERC20.java @@ -0,0 +1,11 @@ +package haveno.asset.tokens; + +import haveno.asset.Erc20Token; + +public class TetherUSDERC20 extends Erc20Token { + public TetherUSDERC20() { + // If you add a new USDT variant or want to change this ticker symbol you should also look here: + // core/src/main/java/haveno/core/provider/price/PriceProvider.java:getAll() + super("Tether USD (ERC20)", "USDT-ERC20"); + } +} diff --git a/assets/src/main/java/haveno/asset/tokens/TetherUSDTRC20.java b/assets/src/main/java/haveno/asset/tokens/TetherUSDTRC20.java new file mode 100644 index 00000000..c5669d12 --- /dev/null +++ b/assets/src/main/java/haveno/asset/tokens/TetherUSDTRC20.java @@ -0,0 +1,11 @@ +package haveno.asset.tokens; + +import haveno.asset.Trc20Token; + +public class TetherUSDTRC20 extends Trc20Token { + public TetherUSDTRC20() { + // If you add a new USDT variant or want to change this ticker symbol you should also look here: + // core/src/main/java/haveno/core/provider/price/PriceProvider.java:getAll() + super("Tether USD (TRC20)", "USDT-TRC20"); + } +} diff --git a/assets/src/main/resources/META-INF/services/haveno.asset.Asset b/assets/src/main/resources/META-INF/services/haveno.asset.Asset index 7108c288..350f6f15 100644 --- a/assets/src/main/resources/META-INF/services/haveno.asset.Asset +++ b/assets/src/main/resources/META-INF/services/haveno.asset.Asset @@ -7,3 +7,5 @@ haveno.asset.coins.BitcoinCash haveno.asset.coins.Ether haveno.asset.coins.Litecoin haveno.asset.coins.Monero +haveno.asset.tokens.TetherUSDERC20 +haveno.asset.tokens.TetherUSDTRC20 \ No newline at end of file diff --git a/assets/src/test/java/haveno/asset/coins/TetherUSDERC20Test.java b/assets/src/test/java/haveno/asset/coins/TetherUSDERC20Test.java new file mode 100644 index 00000000..fd7d97ce --- /dev/null +++ b/assets/src/test/java/haveno/asset/coins/TetherUSDERC20Test.java @@ -0,0 +1,43 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package haveno.asset.coins; + +import haveno.asset.AbstractAssetTest; +import haveno.asset.tokens.TetherUSDERC20; + +import org.junit.jupiter.api.Test; + + public class TetherUSDERC20Test extends AbstractAssetTest { + + public TetherUSDERC20Test() { + super(new TetherUSDERC20()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("0x2a65Aca4D5fC5B5C859090a6c34d164135398226"); + assertValidAddress("2a65Aca4D5fC5B5C859090a6c34d164135398226"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("0x2a65Aca4D5fC5B5C859090a6c34d1641353982266"); + assertInvalidAddress("0x2a65Aca4D5fC5B5C859090a6c34d16413539822g"); + assertInvalidAddress("2a65Aca4D5fC5B5C859090a6c34d16413539822g"); + } + } \ No newline at end of file diff --git a/assets/src/test/java/haveno/asset/coins/TetherUSDTRC20Test.java b/assets/src/test/java/haveno/asset/coins/TetherUSDTRC20Test.java new file mode 100644 index 00000000..7fef554c --- /dev/null +++ b/assets/src/test/java/haveno/asset/coins/TetherUSDTRC20Test.java @@ -0,0 +1,42 @@ +/* + * This file is part of Haveno. + * + * Haveno is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Haveno is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Haveno. If not, see . + */ + +package haveno.asset.coins; + +import haveno.asset.AbstractAssetTest; +import haveno.asset.tokens.TetherUSDTRC20; + +import org.junit.jupiter.api.Test; + + public class TetherUSDTRC20Test extends AbstractAssetTest { + + public TetherUSDTRC20Test() { + super(new TetherUSDTRC20()); + } + + @Test + public void testValidAddresses() { + assertValidAddress("TVnmu3E6DYVL4bpAoZnPNEPVUrgC7eSWaX"); + } + + @Test + public void testInvalidAddresses() { + assertInvalidAddress("0x2a65Aca4D5fC5B5C859090a6c34d1641353982266"); + assertInvalidAddress("0x2a65Aca4D5fC5B5C859090a6c34d16413539822g"); + assertInvalidAddress("2a65Aca4D5fC5B5C859090a6c34d16413539822g"); + } + } \ No newline at end of file diff --git a/core/src/main/java/haveno/core/locale/CurrencyUtil.java b/core/src/main/java/haveno/core/locale/CurrencyUtil.java index 45371225..5775d61d 100644 --- a/core/src/main/java/haveno/core/locale/CurrencyUtil.java +++ b/core/src/main/java/haveno/core/locale/CurrencyUtil.java @@ -200,6 +200,8 @@ public class CurrencyUtil { result.add(new CryptoCurrency("BCH", "Bitcoin Cash")); result.add(new CryptoCurrency("ETH", "Ether")); result.add(new CryptoCurrency("LTC", "Litecoin")); + result.add(new CryptoCurrency("USDT-ERC20", "Tether USD (ERC20)")); + result.add(new CryptoCurrency("USDT-TRC20", "Tether USD (TRC20)")); result.sort(TradeCurrency::compareTo); return result; } @@ -295,6 +297,9 @@ public class CurrencyUtil { if (currencyCode != null && isCryptoCurrencyMap.containsKey(currencyCode.toUpperCase())) { return isCryptoCurrencyMap.get(currencyCode.toUpperCase()); } + if (isCryptoCurrencyBase(currencyCode)) { + return true; + } boolean isCryptoCurrency; if (currencyCode == null) { @@ -321,6 +326,19 @@ public class CurrencyUtil { return isCryptoCurrency; } + private static boolean isCryptoCurrencyBase(String currencyCode) { + if (currencyCode == null) return false; + currencyCode = currencyCode.toUpperCase(); + return currencyCode.equals("USDT"); + } + + public static String getCurrencyCodeBase(String currencyCode) { + if (currencyCode == null) return null; + currencyCode = currencyCode.toUpperCase(); + if (currencyCode.contains("USDT")) return "USDT"; + return currencyCode; + } + public static Optional getCryptoCurrency(String currencyCode) { return Optional.ofNullable(cryptoCurrencyMapSupplier.get().get(currencyCode)); } diff --git a/core/src/main/java/haveno/core/provider/price/PriceFeedService.java b/core/src/main/java/haveno/core/provider/price/PriceFeedService.java index 6f3566d1..c5827779 100644 --- a/core/src/main/java/haveno/core/provider/price/PriceFeedService.java +++ b/core/src/main/java/haveno/core/provider/price/PriceFeedService.java @@ -292,15 +292,16 @@ public class PriceFeedService { @Nullable public MarketPrice getMarketPrice(String currencyCode) { synchronized (cache) { - return cache.getOrDefault(currencyCode, null); + return cache.getOrDefault(CurrencyUtil.getCurrencyCodeBase(currencyCode), null); } } private void setHavenoMarketPrice(String currencyCode, Price price) { UserThread.execute(() -> { + String currencyCodeBase = CurrencyUtil.getCurrencyCodeBase(currencyCode); synchronized (cache) { - if (!cache.containsKey(currencyCode) || !cache.get(currencyCode).isExternallyProvidedPrice()) { - cache.put(currencyCode, new MarketPrice(currencyCode, + if (!cache.containsKey(currencyCodeBase) || !cache.get(currencyCodeBase).isExternallyProvidedPrice()) { + cache.put(currencyCodeBase, new MarketPrice(currencyCodeBase, MathUtils.scaleDownByPowerOf10(price.getValue(), CurrencyUtil.isCryptoCurrency(currencyCode) ? CryptoMoney.SMALLEST_UNIT_EXPONENT : TraditionalMoney.SMALLEST_UNIT_EXPONENT), 0, false)); diff --git a/core/src/main/java/haveno/core/provider/price/PriceProvider.java b/core/src/main/java/haveno/core/provider/price/PriceProvider.java index 17bef33a..93162947 100644 --- a/core/src/main/java/haveno/core/provider/price/PriceProvider.java +++ b/core/src/main/java/haveno/core/provider/price/PriceProvider.java @@ -21,6 +21,7 @@ import com.google.gson.Gson; import com.google.gson.internal.LinkedTreeMap; import haveno.common.app.Version; import haveno.common.util.MathUtils; +import haveno.core.locale.CurrencyUtil; import haveno.core.provider.HttpClientProvider; import haveno.network.http.HttpClient; import haveno.network.p2p.P2PService; @@ -63,6 +64,7 @@ public class PriceProvider extends HttpClientProvider { String baseCurrencyCode = (String) treeMap.get("baseCurrencyCode"); String counterCurrencyCode = (String) treeMap.get("counterCurrencyCode"); String currencyCode = baseCurrencyCode.equals("XMR") ? counterCurrencyCode : baseCurrencyCode; + currencyCode = CurrencyUtil.getCurrencyCodeBase(currencyCode); double price = (Double) treeMap.get("price"); // json uses double for our timestampSec long value... long timestampSec = MathUtils.doubleToLong((Double) treeMap.get("timestampSec")); diff --git a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java index 33233083..66ca6475 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/MutableOfferViewModel.java @@ -268,7 +268,7 @@ public abstract class MutableOfferViewModel ext dataModel.getTradeCurrencyCode())); } volumePromptLabel.bind(createStringBinding( - () -> Res.get("createOffer.volume.prompt", dataModel.getTradeCurrencyCode().get()), + () -> Res.get("createOffer.volume.prompt", CurrencyUtil.getCurrencyCodeBase(dataModel.getTradeCurrencyCode().get())), dataModel.getTradeCurrencyCode())); totalToPay.bind(createStringBinding(() -> HavenoUtils.formatXmr(dataModel.totalToPayAsProperty().get(), true), diff --git a/desktop/src/main/java/haveno/desktop/main/presentation/MarketPricePresentation.java b/desktop/src/main/java/haveno/desktop/main/presentation/MarketPricePresentation.java index ab77b5e0..35293692 100644 --- a/desktop/src/main/java/haveno/desktop/main/presentation/MarketPricePresentation.java +++ b/desktop/src/main/java/haveno/desktop/main/presentation/MarketPricePresentation.java @@ -110,9 +110,19 @@ public class MarketPricePresentation { } private void fillPriceFeedComboBoxItems() { - List currencyItems = preferences.getTradeCurrenciesAsObservable() + + // collect unique currency code bases + List uniqueCurrencyCodeBases = preferences.getTradeCurrenciesAsObservable() .stream() - .map(tradeCurrency -> new PriceFeedComboBoxItem(tradeCurrency.getCode())) + .map(TradeCurrency::getCode) + .map(CurrencyUtil::getCurrencyCodeBase) + .distinct() + .collect(Collectors.toList()); + + // create price feed items + List currencyItems = uniqueCurrencyCodeBases + .stream() + .map(currencyCodeBase -> new PriceFeedComboBoxItem(currencyCodeBase)) .collect(Collectors.toList()); priceFeedComboBoxItems.setAll(currencyItems); } @@ -171,7 +181,7 @@ public class MarketPricePresentation { private Optional findPriceFeedComboBoxItem(String currencyCode) { return priceFeedComboBoxItems.stream() - .filter(item -> item.currencyCode.equals(currencyCode)) + .filter(item -> CurrencyUtil.getCurrencyCodeBase(item.currencyCode).equals(CurrencyUtil.getCurrencyCodeBase(currencyCode))) .findAny(); } From 9c3e405fe08a2ab1772a8757674bb5ebb6ce13f2 Mon Sep 17 00:00:00 2001 From: woodser Date: Sun, 10 Nov 2024 17:33:18 -0500 Subject: [PATCH 41/46] get market price by currency code base e.g. usdt --- core/src/main/java/haveno/core/api/CorePriceService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/haveno/core/api/CorePriceService.java b/core/src/main/java/haveno/core/api/CorePriceService.java index 453236c5..ddd194eb 100644 --- a/core/src/main/java/haveno/core/api/CorePriceService.java +++ b/core/src/main/java/haveno/core/api/CorePriceService.java @@ -72,7 +72,7 @@ class CorePriceService { * @return Price per 1 XMR in the given currency (traditional or crypto) */ public double getMarketPrice(String currencyCode) throws ExecutionException, InterruptedException, TimeoutException, IllegalArgumentException { - var marketPrice = priceFeedService.requestAllPrices().get(currencyCode); + var marketPrice = priceFeedService.requestAllPrices().get(CurrencyUtil.getCurrencyCodeBase(currencyCode)); if (marketPrice == null) { throw new IllegalArgumentException("Currency not found: " + currencyCode); // message sent to client } From 3cdfa0fa27372fd738271171a2bdceb2ff47888a Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 11 Nov 2024 06:49:04 -0500 Subject: [PATCH 42/46] fix equals and hashcode of trade, crypto, and traditional currencies --- .../haveno/core/locale/CryptoCurrency.java | 2 -- .../haveno/core/locale/TradeCurrency.java | 22 ++++++++++++++++--- .../core/locale/TraditionalCurrency.java | 2 -- .../settings/preferences/PreferencesView.java | 2 +- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/haveno/core/locale/CryptoCurrency.java b/core/src/main/java/haveno/core/locale/CryptoCurrency.java index bbc5b4a4..6c46c9d2 100644 --- a/core/src/main/java/haveno/core/locale/CryptoCurrency.java +++ b/core/src/main/java/haveno/core/locale/CryptoCurrency.java @@ -19,10 +19,8 @@ package haveno.core.locale; import com.google.protobuf.Message; -import lombok.EqualsAndHashCode; import lombok.Getter; -@EqualsAndHashCode(callSuper = true) public final class CryptoCurrency extends TradeCurrency { // http://boschista.deviantart.com/journal/Cool-ASCII-Symbols-214218618 private final static String PREFIX = "✦ "; diff --git a/core/src/main/java/haveno/core/locale/TradeCurrency.java b/core/src/main/java/haveno/core/locale/TradeCurrency.java index a9da96ea..b60abf5e 100644 --- a/core/src/main/java/haveno/core/locale/TradeCurrency.java +++ b/core/src/main/java/haveno/core/locale/TradeCurrency.java @@ -19,19 +19,16 @@ package haveno.core.locale; import haveno.common.proto.ProtobufferRuntimeException; import haveno.common.proto.persistable.PersistablePayload; -import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; -@EqualsAndHashCode @ToString @Getter @Slf4j public abstract class TradeCurrency implements PersistablePayload, Comparable { protected final String code; - @EqualsAndHashCode.Exclude protected final String name; public TradeCurrency(String code, String name) { @@ -82,4 +79,23 @@ public abstract class TradeCurrency implements PersistablePayload, Comparable Date: Mon, 11 Nov 2024 06:52:13 -0500 Subject: [PATCH 43/46] remove USDT-TRC20 from 'main' cryptos --- core/src/main/java/haveno/core/locale/CurrencyUtil.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/main/java/haveno/core/locale/CurrencyUtil.java b/core/src/main/java/haveno/core/locale/CurrencyUtil.java index 5775d61d..93f579f4 100644 --- a/core/src/main/java/haveno/core/locale/CurrencyUtil.java +++ b/core/src/main/java/haveno/core/locale/CurrencyUtil.java @@ -201,7 +201,6 @@ public class CurrencyUtil { result.add(new CryptoCurrency("ETH", "Ether")); result.add(new CryptoCurrency("LTC", "Litecoin")); result.add(new CryptoCurrency("USDT-ERC20", "Tether USD (ERC20)")); - result.add(new CryptoCurrency("USDT-TRC20", "Tether USD (TRC20)")); result.sort(TradeCurrency::compareTo); return result; } From dd5b7996b3224c6b2701ca352018276579a8a2cf Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 11 Nov 2024 07:22:56 -0500 Subject: [PATCH 44/46] fix price provider selection, remove local provider for production --- core/src/main/java/haveno/core/offer/Offer.java | 2 +- .../main/java/haveno/core/provider/ProvidersRepository.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/haveno/core/offer/Offer.java b/core/src/main/java/haveno/core/offer/Offer.java index b3c81c5b..675fbeb5 100644 --- a/core/src/main/java/haveno/core/offer/Offer.java +++ b/core/src/main/java/haveno/core/offer/Offer.java @@ -200,7 +200,7 @@ public class Offer implements NetworkPayload, PersistablePayload { return null; } } else { - log.warn("We don't have a market price. " + + log.trace("We don't have a market price. " + "That case could only happen if you don't have a price feed."); return null; } diff --git a/core/src/main/java/haveno/core/provider/ProvidersRepository.java b/core/src/main/java/haveno/core/provider/ProvidersRepository.java index a31794a0..8357bb7f 100644 --- a/core/src/main/java/haveno/core/provider/ProvidersRepository.java +++ b/core/src/main/java/haveno/core/provider/ProvidersRepository.java @@ -93,7 +93,7 @@ public class ProvidersRepository { fillProviderList(); // select next provider if current provider is null or banned - if (baseUrl == null || isBanned(baseUrl)) selectNextProviderBaseUrl(); + if (baseUrl.isEmpty() || isBanned(baseUrl)) selectNextProviderBaseUrl(); if (bannedNodes != null && !bannedNodes.isEmpty()) { log.info("Excluded provider nodes from filter: nodes={}, selected provider baseUrl={}, providerList={}", bannedNodes, baseUrl, providerList); @@ -140,7 +140,7 @@ public class ProvidersRepository { "http://173.230.142.36:8078/"); } else { providers = new ArrayList(); - providers.add(DEFAULT_LOCAL_NODE); // try local provider first + //providers.add(DEFAULT_LOCAL_NODE); // try local provider first providers.addAll(DEFAULT_NODES); } } else { From 7a392f3c1ee39fbe1da6114841bc31710fcf191c Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 11 Nov 2024 11:13:22 -0500 Subject: [PATCH 45/46] defer payout publish if published, skip warning log if published --- .../main/java/haveno/core/trade/Trade.java | 19 ++++++++++++------- .../tasks/ProcessPaymentReceivedMessage.java | 11 +++++------ .../SellerSendPaymentReceivedMessage.java | 3 ++- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index e7b7584e..b5403511 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -1085,11 +1085,12 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { } catch (IllegalArgumentException | IllegalStateException e) { throw e; } catch (Exception e) { - log.warn("Failed to import multisig hex, tradeId={}, attempt={}/{}, error={}", getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage()); handleWalletError(e, sourceConnection); + doPollWallet(); + if (isPayoutPublished()) break; + log.warn("Failed to import multisig hex, tradeId={}, attempt={}/{}, error={}", getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage()); if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying - doPollWallet(); } } } @@ -1205,8 +1206,10 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { } catch (IllegalArgumentException | IllegalStateException e) { throw e; } catch (Exception e) { - log.warn("Failed to create payout tx, tradeId={}, attempt={}/{}, error={}", getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage()); handleWalletError(e, sourceConnection); + doPollWallet(); + if (isPayoutPublished()) break; + log.warn("Failed to create payout tx, tradeId={}, attempt={}/{}, error={}", getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage()); if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying } @@ -1265,11 +1268,12 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { throw e; } catch (Exception e) { if (e.getMessage().contains("not possible")) throw new IllegalArgumentException("Loser payout is too small to cover the mining fee"); - log.warn("Failed to create dispute payout tx, tradeId={}, attempt={}/{}, error={}", getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage()); handleWalletError(e, sourceConnection); + doPollWallet(); + if (isPayoutPublished()) break; + log.warn("Failed to create dispute payout tx, tradeId={}, attempt={}/{}, error={}", getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage()); if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying - doPollWallet(); } } throw new RuntimeException("Failed to create payout tx for " + getClass().getSimpleName() + " " + getId()); @@ -1295,11 +1299,12 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { } catch (IllegalArgumentException | IllegalStateException e) { throw e; } catch (Exception e) { - log.warn("Failed to process payout tx, tradeId={}, attempt={}/{}, error={}", getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage(), e); handleWalletError(e, sourceConnection); + doPollWallet(); + if (isPayoutPublished()) break; + log.warn("Failed to process payout tx, tradeId={}, attempt={}/{}, error={}", getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage(), e); if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying - doPollWallet(); } finally { requestSaveWallet(); requestPersistence(); diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java index 121d87d8..2ce29828 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java @@ -143,12 +143,10 @@ public class ProcessPaymentReceivedMessage extends TradeTask { // handle if payout tx not published if (!trade.isPayoutPublished()) { - // wait to sign and publish payout tx if defer flag set (seller recently saw payout tx arrive at buyer) - boolean isSigned = message.getSignedPayoutTxHex() != null; - boolean deferSignAndPublish = trade instanceof ArbitratorTrade && !isSigned && message.isDeferPublishPayout(); - if (deferSignAndPublish) { - log.info("Deferring signing and publishing payout tx for {} {}", trade.getClass().getSimpleName(), trade.getId()); - trade.pollWalletNormallyForMs(60000); + // wait to publish payout tx if defer flag set from seller (payout is expected) + if (message.isDeferPublishPayout()) { + log.info("Deferring publishing payout tx for {} {}", trade.getClass().getSimpleName(), trade.getId()); + if (trade instanceof ArbitratorTrade) trade.pollWalletNormallyForMs(60000); // stop idling arbitrator for (int i = 0; i < 5; i++) { if (trade.isPayoutPublished()) break; HavenoUtils.waitFor(Trade.DEFER_PUBLISH_MS / 5); @@ -159,6 +157,7 @@ public class ProcessPaymentReceivedMessage extends TradeTask { // verify and publish payout tx if (!trade.isPayoutPublished()) { try { + boolean isSigned = message.getSignedPayoutTxHex() != null; if (isSigned) { log.info("{} {} publishing signed payout tx from seller", trade.getClass().getSimpleName(), trade.getId()); trade.processPayoutTx(message.getSignedPayoutTxHex(), false, true); diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java b/core/src/main/java/haveno/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java index cbfa0e32..f08fe879 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java @@ -103,6 +103,7 @@ public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessag // messages where only the one which gets processed by the peer would be removed we use the same uid. All // other data stays the same when we re-send the message at any time later. String deterministicId = HavenoUtils.getDeterministicId(trade, PaymentReceivedMessage.class, getReceiverNodeAddress()); + boolean deferPublishPayout = trade.isPayoutPublished() || trade.getState().ordinal() >= Trade.State.SELLER_SAW_ARRIVED_PAYMENT_RECEIVED_MSG.ordinal(); // informs receiver to expect payout so delay processing PaymentReceivedMessage message = new PaymentReceivedMessage( tradeId, processModel.getMyNodeAddress(), @@ -110,7 +111,7 @@ public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessag trade.getPayoutTxHex() == null ? trade.getSelf().getUnsignedPayoutTxHex() : null, // unsigned // TODO: phase in after next update to clear old style trades trade.getPayoutTxHex() == null ? null : trade.getPayoutTxHex(), // signed trade.getSelf().getUpdatedMultisigHex(), - trade.getState().ordinal() >= Trade.State.SELLER_SAW_ARRIVED_PAYMENT_RECEIVED_MSG.ordinal(), // informs to expect payout + deferPublishPayout, trade.getTradePeer().getAccountAgeWitness(), signedWitness, getReceiver() == trade.getArbitrator() ? trade.getBuyer().getPaymentSentMessage() : null // buyer already has payment sent message From ea8badd3f64c097c434f1de6ac40c3088535e19b Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 11 Nov 2024 18:19:55 -0500 Subject: [PATCH 46/46] bump version to 1.0.13 --- build.gradle | 2 +- common/src/main/java/haveno/common/app/Version.java | 2 +- desktop/package/macosx/Info.plist | 4 ++-- seednode/src/main/java/haveno/seednode/SeedNodeMain.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle b/build.gradle index ce796bc2..81a315ad 100644 --- a/build.gradle +++ b/build.gradle @@ -610,7 +610,7 @@ configure(project(':desktop')) { apply plugin: 'com.github.johnrengelman.shadow' apply from: 'package/package.gradle' - version = '1.0.12-SNAPSHOT' + version = '1.0.13-SNAPSHOT' jar.manifest.attributes( "Implementation-Title": project.name, diff --git a/common/src/main/java/haveno/common/app/Version.java b/common/src/main/java/haveno/common/app/Version.java index 6708f17d..14e5a6c7 100644 --- a/common/src/main/java/haveno/common/app/Version.java +++ b/common/src/main/java/haveno/common/app/Version.java @@ -28,7 +28,7 @@ import static com.google.common.base.Preconditions.checkArgument; public class Version { // The application versions // We use semantic versioning with major, minor and patch - public static final String VERSION = "1.0.12"; + public static final String VERSION = "1.0.13"; /** * Holds a list of the tagged resource files for optimizing the getData requests. diff --git a/desktop/package/macosx/Info.plist b/desktop/package/macosx/Info.plist index 138ca4f7..2d4618f7 100644 --- a/desktop/package/macosx/Info.plist +++ b/desktop/package/macosx/Info.plist @@ -5,10 +5,10 @@ CFBundleVersion - 1.0.12 + 1.0.13 CFBundleShortVersionString - 1.0.12 + 1.0.13 CFBundleExecutable Haveno diff --git a/seednode/src/main/java/haveno/seednode/SeedNodeMain.java b/seednode/src/main/java/haveno/seednode/SeedNodeMain.java index 51a85bbc..b1da3d3e 100644 --- a/seednode/src/main/java/haveno/seednode/SeedNodeMain.java +++ b/seednode/src/main/java/haveno/seednode/SeedNodeMain.java @@ -41,7 +41,7 @@ import lombok.extern.slf4j.Slf4j; @Slf4j public class SeedNodeMain extends ExecutableForAppWithP2p { private static final long CHECK_CONNECTION_LOSS_SEC = 30; - private static final String VERSION = "1.0.12"; + private static final String VERSION = "1.0.13"; private SeedNode seedNode; private Timer checkConnectionLossTime;