From b3d3a936d567bb59a12695593e9a2772aae15700 Mon Sep 17 00:00:00 2001 From: woodser Date: Sun, 11 Aug 2024 19:27:19 -0400 Subject: [PATCH 01/18] remove jtorctl from verification metadata --- gradle/verification-metadata.xml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 5e2bf6c5..1b0d95e8 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -173,14 +173,6 @@ - - - - - - - - @@ -209,9 +201,6 @@ - - - From 38b1ace4a43bae3b766023b01afca90a804be926 Mon Sep 17 00:00:00 2001 From: woodser Date: Sun, 11 Aug 2024 19:27:31 -0400 Subject: [PATCH 02/18] document changing p2p network code for new networks --- docs/create-mainnet.md | 6 ++++++ docs/deployment-guide.md | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/docs/create-mainnet.md b/docs/create-mainnet.md index 32606a0e..38c211d4 100644 --- a/docs/create-mainnet.md +++ b/docs/create-mainnet.md @@ -57,6 +57,12 @@ For example, change "Haveno" to "HavenoX", which will use this application folde - macOS: ~/Library/Application Support/HavenoX/ - Windows: ~\AppData\Roaming\HavenoX\ +## Change the P2P network version + +To avoid interference with other networks, change `P2P_NETWORK_VERSION` in [Version.java](https://github.com/haveno-dex/haveno/blob/a7e90395d24ec3d33262dd5d09c5faec61651a51/common/src/main/java/haveno/common/app/Version.java#L83). + +For example, change it to `"B"`. + ## Start the seed nodes Rebuild for the previous changes to the source code to take effect: `make skip-tests`. diff --git a/docs/deployment-guide.md b/docs/deployment-guide.md index ce538319..2f7590d0 100644 --- a/docs/deployment-guide.md +++ b/docs/deployment-guide.md @@ -219,6 +219,12 @@ For example, change "Haveno" to "HavenoX", which will use this application folde - macOS: ~/Library/Application Support/HavenoX/ - Windows: ~\AppData\Roaming\HavenoX\ +## Change the P2P network version + +To avoid interference with other networks, change `P2P_NETWORK_VERSION` in [Version.java](https://github.com/haveno-dex/haveno/blob/a7e90395d24ec3d33262dd5d09c5faec61651a51/common/src/main/java/haveno/common/app/Version.java#L83). + +For example, change it to `"B"`. + ## Set the network's release date Optionally set the network's approximate release date by setting `RELEASE_DATE` in HavenoUtils.java. From ffe88b49a659e81e546cb95cd9dfeaf46e50555e Mon Sep 17 00:00:00 2001 From: shortwavesurfer2009 <116814522+shortwavesurfer2009@users.noreply.github.com> Date: Wed, 14 Aug 2024 07:47:20 -0400 Subject: [PATCH 03/18] Lower PoW activation threshold --- seednode/torrc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/seednode/torrc b/seednode/torrc index d90b0111..d6e2c55b 100644 --- a/seednode/torrc +++ b/seednode/torrc @@ -124,8 +124,8 @@ HiddenServiceEnableIntroDoSDefense 1 ## Proof of Work (PoW) before establishing Rendezvous Circuits ## The lower the queue and burst rates, the higher the puzzle effort tends to be for users. HiddenServicePoWDefensesEnabled 1 -HiddenServicePoWQueueRate 200 # (Default: 250) -HiddenServicePoWQueueBurst 1000 # (Default: 2500) +HiddenServicePoWQueueRate 50 # (Default: 250) +HiddenServicePoWQueueBurst 250 # (Default: 2500) ## Stream limits in the established Rendezvous Circuits ## The maximum number of simultaneous streams (connections) per rendezvous circuit. The max value allowed is 65535. (0 = unlimited) @@ -143,8 +143,8 @@ HiddenServiceEnableIntroDoSDefense 1 #HiddenServiceNumIntroductionPoints 3 # (Default: 3) HiddenServicePoWDefensesEnabled 1 -HiddenServicePoWQueueRate 200 # (Default: 250) -HiddenServicePoWQueueBurst 1000 # (Default: 2500) +HiddenServicePoWQueueRate 50 # (Default: 250) +HiddenServicePoWQueueBurst 250 # (Default: 2500) HiddenServiceMaxStreams 25 #HiddenServiceMaxStreamsCloseCircuit 1 From 5f6f513f3c6de36fad598ce0ab00f4843ff1fc8d Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 16 Aug 2024 12:19:16 -0400 Subject: [PATCH 04/18] Revert "remove jtorctl from verification metadata" This reverts commit b3d3a936d567bb59a12695593e9a2772aae15700. --- gradle/verification-metadata.xml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 1b0d95e8..5e2bf6c5 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -173,6 +173,14 @@ + + + + + + + + @@ -201,6 +209,9 @@ + + + From 1046caf907d099ce77951b84df000ba65f384c4c Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 16 Aug 2024 13:35:43 -0400 Subject: [PATCH 05/18] update jtorctl version --- build.gradle | 2 +- gradle/verification-metadata.xml | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/build.gradle b/build.gradle index 06020557..4aad0677 100644 --- a/build.gradle +++ b/build.gradle @@ -316,7 +316,7 @@ configure(project(':common')) { exclude(module: 'guava') exclude(module: 'animal-sniffer-annotations') } - + implementation(group: 'com.github.bisq-network', name: 'jtorctl') { version { strictly "[b2a172f44edcd8deaa5ed75d936dcbb007f0d774]" } } implementation "org.openjfx:javafx-base:$javafxVersion:$os" implementation "org.openjfx:javafx-graphics:$javafxVersion:$os" } diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 5e2bf6c5..dc626c93 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -173,14 +173,6 @@ - - - - - - - - From 8cb8f9f3cfdebeba1fdea68c2dac7be7e7495257 Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 16 Aug 2024 14:04:07 -0400 Subject: [PATCH 06/18] exclude jtorctl from JesusMcCloud --- build.gradle | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 4aad0677..03a970ee 100644 --- a/build.gradle +++ b/build.gradle @@ -316,7 +316,12 @@ configure(project(':common')) { exclude(module: 'guava') exclude(module: 'animal-sniffer-annotations') } - implementation(group: 'com.github.bisq-network', name: 'jtorctl') { version { strictly "[b2a172f44edcd8deaa5ed75d936dcbb007f0d774]" } } + + // override transitive dependency and use latest version from bisq + implementation(group: 'com.github.bisq-network', name: 'jtorctl') { + version { strictly "[b2a172f44edcd8deaa5ed75d936dcbb007f0d774]" } + exclude group: 'com.github.JesusMcCloud', module: 'jtorctl' + } implementation "org.openjfx:javafx-base:$javafxVersion:$os" implementation "org.openjfx:javafx-graphics:$javafxVersion:$os" } From d3d30c3d0b12351d6126547c9696e09a393f2366 Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 16 Aug 2024 16:18:09 -0400 Subject: [PATCH 07/18] exclude jtorctl from JesusMcCloud globally --- build.gradle | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index 03a970ee..ddb0b929 100644 --- a/build.gradle +++ b/build.gradle @@ -89,6 +89,10 @@ configure(subprojects) { maven { url 'https://mvnrepository.com' } } + configurations.all { + exclude group: 'com.github.JesusMcCloud', module: 'jtorctl' + } + tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } @@ -318,10 +322,7 @@ configure(project(':common')) { } // override transitive dependency and use latest version from bisq - implementation(group: 'com.github.bisq-network', name: 'jtorctl') { - version { strictly "[b2a172f44edcd8deaa5ed75d936dcbb007f0d774]" } - exclude group: 'com.github.JesusMcCloud', module: 'jtorctl' - } + implementation(group: 'com.github.bisq-network', name: 'jtorctl') { version { strictly "[b2a172f44edcd8deaa5ed75d936dcbb007f0d774]" } } implementation "org.openjfx:javafx-base:$javafxVersion:$os" implementation "org.openjfx:javafx-graphics:$javafxVersion:$os" } From d3ee7340f0ef284b734acf9ba047f05a488b3113 Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 16 Aug 2024 17:23:49 -0400 Subject: [PATCH 08/18] remove jtorctl from JesusMcCloud --- build.gradle | 4 ---- 1 file changed, 4 deletions(-) diff --git a/build.gradle b/build.gradle index ddb0b929..4a24e5fc 100644 --- a/build.gradle +++ b/build.gradle @@ -89,10 +89,6 @@ configure(subprojects) { maven { url 'https://mvnrepository.com' } } - configurations.all { - exclude group: 'com.github.JesusMcCloud', module: 'jtorctl' - } - tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } From e18d281ca97aec79a75863f7cd71b1a57413950f Mon Sep 17 00:00:00 2001 From: Rico Date: Tue, 20 Aug 2024 14:30:03 +0200 Subject: [PATCH 09/18] Add "UI Scaling" section to the user guide --- docs/user-guide.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/user-guide.md b/docs/user-guide.md index a7072ca6..8ea7db1b 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -2,10 +2,24 @@ This document is a guide for Haveno users. -# Running a Local Monero Node +## Running a Local Monero Node For the best experience using Haveno, it is highly recommended to run your own local Monero node to improve security and responsiveness. By default, Haveno will automatically connect to a local node if it is detected. Additionally, Haveno will automatically start and connect to your local Monero node if it was last used and is currently offline. -Otherwise, Haveno will connect to a pre-configured remote node, unless manually configured otherwise. \ No newline at end of file +Otherwise, Haveno will connect to a pre-configured remote node, unless manually configured otherwise. + +## UI Scaling For High DPI Displays + +If the UI is too small on your display, you can force UI scaling to a value of your choice using one of the following approaches. The examples below scale the UI to 200%, you can replace the '2' with a value of your choice, e.g. '1.5' for 150%. + +### Edit The Application Shortcut (KDE Plasma) + +1) Open the properties of your shortcut to haveno +2) Click on Program +3) Add `JAVA_TOOL_OPTIONS=-Dglass.gtk.uiScale=2` to the environment variables + +### Launching From The Command Line + +Prepend `JAVA_TOOL_OPTIONS=-Dglass.gtk.uiScale=2` to the command you use to launch haveno (e.g. `JAVA_TOOL_OPTIONS=-Dglass.gtk.uiScale=2 haveno-desktop`). From caebd58303569aa7ab316c55539eb37258b3e107 Mon Sep 17 00:00:00 2001 From: woodser Date: Tue, 20 Aug 2024 08:30:48 -0400 Subject: [PATCH 10/18] preserve withdraw fields when page reactivated while in progress --- .../desktop/main/funds/withdrawal/WithdrawalView.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalView.java b/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalView.java index 0fa45624..cc5a7cd1 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalView.java @@ -242,8 +242,12 @@ public class WithdrawalView extends ActivatableView { if (GUIUtil.isReadyForTxBroadcastOrShowPopup(xmrWalletService)) { try { + // collect tx fields to local variables + String withdrawToAddress = withdrawToTextField.getText(); + boolean sendMax = this.sendMax; + BigInteger amount = this.amount; + // validate address - final String withdrawToAddress = withdrawToTextField.getText(); if (!MoneroUtils.isValidAddress(withdrawToAddress, XmrWalletService.getMoneroNetworkType())) { throw new IllegalArgumentException(Res.get("validation.xmr.invalidAddress")); } @@ -298,7 +302,7 @@ public class WithdrawalView extends ActivatableView { BigInteger receiverAmount = tx.getOutgoingTransfer().getDestinations().get(0).getAmount(); BigInteger fee = tx.getFee(); String messageText = Res.get("shared.sendFundsDetailsWithFee", - HavenoUtils.formatXmr(amount, true), + HavenoUtils.formatXmr(receiverAmount.add(fee), true), withdrawToAddress, HavenoUtils.formatXmr(fee, true), HavenoUtils.formatXmr(receiverAmount, true)); From d74b0995d0c7ac33605fd1827e3c24ef8cbead62 Mon Sep 17 00:00:00 2001 From: woodser Date: Sat, 24 Aug 2024 13:45:42 -0400 Subject: [PATCH 11/18] update build instructions for mainnet --- docs/installing.md | 60 ++++++++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/docs/installing.md b/docs/installing.md index f779d8ef..dcf9c81d 100644 --- a/docs/installing.md +++ b/docs/installing.md @@ -1,18 +1,19 @@ -# Set up environment +# Build and run Haveno -These are the steps needed to build Haveno and test it on our test network or locally. +These are the steps needed to build and run Haveno. You can test it locally or on our test network using the official Haveno repository. + +> [!note] +> Trying to use Haveno on mainnet? +> +> The official Haveno repository does not operate or endorse any mainnet network. +> +> Find a third party network and use their installer or build their repository. Alternatively [create your own mainnet network](create-mainnet.md). ## Install dependencies -On Linux and macOS, install Java JDK 21: - -``` -curl -s "https://get.sdkman.io" | bash -sdk install java 21.0.2.fx-librca -``` - -On Windows, install MSYS2 and Java JDK 21: +On Ubuntu: `sudo apt install make wget git` +On Windows, first install MSYS2: 1. Install [MSYS2](https://www.msys2.org/). 2. Start MSYS2 MINGW64 or MSYS MINGW32 depending on your system. Use MSYS2 for all commands throughout this document. 4. Update pacman: `pacman -Syy` @@ -21,12 +22,17 @@ On Windows, install MSYS2 and Java JDK 21: 64-bit: `pacman -S mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake git` 32-bit: `pacman -S mingw-w64-i686-toolchain make mingw-w64-i686-cmake git` - 6. `curl -s "https://get.sdkman.io" | bash` - 7. `sdk install java 21.0.2.fx-librca` + +On all platforms, install Java JDK 21: + +``` +curl -s "https://get.sdkman.io" | bash +sdk install java 21.0.2.fx-librca +``` ## Build Haveno -If it's the first time you are building Haveno, run the following commands to download the repository, the needed dependencies, and build the latest release: +If it's the first time you are building Haveno, run the following commands to download the repository, the needed dependencies, and build the latest release. If using a third party network, replace the repository URL with theirs: ``` git clone https://github.com/haveno-dex/haveno.git @@ -45,15 +51,23 @@ git pull make clean && make ``` -Make sure to delete the folder with the local settings, as there are breaking changes between releases: +## Run Haveno -On **Linux**: remove everything inside `~/.local/share/haveno-*/xmr_stagenet/`, except the `wallet` folder. +> [!note] +> When you run Haveno, your application folder will be installed to: +> * Linux: `~/.local/share/Haveno/` +> * macOS: `~/Library/Application\ Support/Haveno/` +> * Windows: `~\AppData\Roaming\Haveno\` -On **Mac**: remove everything inside `~/Library/Application\ Support/haveno-*/xmr_stagenet/`, except the `wallet` folder. +### Mainnet -On **Windows**: remove everything inside `~\AppData\Roaming\haveno-*/xmr_stagenet/`, except the `wallet` folder. +If you are building a third party repository which supports mainnet, you can start Haveno with: -## Join the public test network +``` +make haveno-desktop-mainnet +``` + +### Join the public test network If you want to try Haveno in a live setup, launch a Haveno instance that will connect to other peers on our public test environment, which runs on Monero's stagenet (you won't need to download the blockchain locally). You'll be able to make test trades with other users and have a preview of Haveno's trade protocol in action. Note that development is very much ongoing. Things are slow and might break. @@ -67,11 +81,11 @@ Steps: 6. Now if you are taking a trade you'll be asked to confirm you have sent the payment outside Haveno. Confirm in the app and wait for the confirmation of received payment from the other trader. 7. Once the other trader confirms, deposits are sent back to the owners and the trade is complete. -# Run a local test network +### Run a local test network If you are a developer who wants to test Haveno in a more controlled way, follow the next steps to build a local test environment. -## Run a local XMR testnet +#### Run a local XMR testnet 1. In a new terminal window run `make monerod1-local` 1. In a new terminal window run `make monerod2-local` @@ -79,7 +93,7 @@ If you are a developer who wants to test Haveno in a more controlled way, follow `start_mining 9tsUiG9bwcU7oTbAdBwBk2PzxFtysge5qcEsHEpetmEKgerHQa1fDqH7a4FiquZmms7yM22jdifVAD7jAb2e63GSJMuhY75 1` -## Deploy +#### Deploy If you are a *screen* user, simply run `make deploy`. This command will open all needed Haveno instances (seednode, user1, user2, arbitrator) using *screen*. @@ -93,7 +107,7 @@ If you don't use *screen*, open 4 terminal windows and run in each one of them: If this is the first time launching the arbitrator desktop application, register the arbitrator after the interface opens. Go to the *Account* tab and press `cmd+r`. Confirm the registration of the arbitrator. -## Fund your wallets +#### Fund your wallets When running user1 and user2, you'll see a Monero address prompted in the terminal. Send test XMR to the addresses of both user1 and user2 to be able to initiate a trade. @@ -101,6 +115,6 @@ You can fund the two wallets by mining some test XMR coins to those addresses. T monerod will start mining local testnet coins on your device using one thread. Replace `ADDRESS` with the address of user1 first, and then user2's. Run `stop_mining` to stop mining. -## Start testing +#### Start testing You are all set. Now that everything is running and your wallets are funded, you can create test trades between user1 and user2. Remember to mine a few blocks after opening and accepting the test trade so the transaction will be confirmed. From 6759687046a3f23ead0fd183e6e9e5622aa1f066 Mon Sep 17 00:00:00 2001 From: woodser Date: Wed, 28 Aug 2024 14:52:48 -0400 Subject: [PATCH 12/18] fix logs for repeated attempts --- .../core/offer/placeoffer/tasks/MakerReserveOfferFunds.java | 2 +- core/src/main/java/haveno/core/trade/Trade.java | 6 +++--- .../trade/protocol/tasks/MaybeSendSignContractRequest.java | 2 +- .../core/trade/protocol/tasks/TakerReserveTradeFunds.java | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerReserveOfferFunds.java b/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerReserveOfferFunds.java index 7bfbda31..4b44a810 100644 --- a/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerReserveOfferFunds.java +++ b/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerReserveOfferFunds.java @@ -87,7 +87,7 @@ public class MakerReserveOfferFunds extends Task { //if (true) throw new RuntimeException("Pretend error"); reserveTx = model.getXmrWalletService().createReserveTx(penaltyFee, makerFee, sendAmount, securityDeposit, returnAddress, openOffer.isReserveExactAmount(), preferredSubaddressIndex); } catch (Exception e) { - log.warn("Error creating reserve tx, offerId={}, attempt={}/{}, error={}", i + 1, TradeProtocol.MAX_ATTEMPTS, openOffer.getShortId(), e.getMessage()); + log.warn("Error creating reserve tx, offerId={}, attempt={}/{}, error={}", openOffer.getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage()); model.getXmrWalletService().handleWalletError(e, sourceConnection); verifyPending(); if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index 4b3aed5b..143f000f 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -1190,7 +1190,7 @@ 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={}", i + 1, TradeProtocol.MAX_ATTEMPTS, getShortId(), e.getMessage()); + log.warn("Failed to create payout tx, tradeId={}, attempt={}/{}, error={}", getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage()); handleWalletError(e, sourceConnection); if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying @@ -1250,7 +1250,7 @@ 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={}", i + 1, TradeProtocol.MAX_ATTEMPTS, getShortId(), e.getMessage()); + log.warn("Failed to create dispute payout tx, tradeId={}, attempt={}/{}, error={}", getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage()); handleWalletError(e, sourceConnection); if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying @@ -1279,7 +1279,7 @@ 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, attempt={}/{}, tradeId={}, error={}", i + 1, TradeProtocol.MAX_ATTEMPTS, getShortId(), e.getMessage()); + log.warn("Failed to process payout tx, tradeId={}, attempt={}/{}, error={}", getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage()); handleWalletError(e, sourceConnection); if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/MaybeSendSignContractRequest.java b/core/src/main/java/haveno/core/trade/protocol/tasks/MaybeSendSignContractRequest.java index 6dcef7f0..69d1620a 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/MaybeSendSignContractRequest.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/MaybeSendSignContractRequest.java @@ -104,7 +104,7 @@ public class MaybeSendSignContractRequest extends TradeTask { try { depositTx = trade.getXmrWalletService().createDepositTx(trade, reserveExactAmount, subaddressIndex); } catch (Exception e) { - log.warn("Error creating deposit tx, attempt={}/{}, tradeId={}, error={}", i + 1, TradeProtocol.MAX_ATTEMPTS, trade.getShortId(), e.getMessage()); + log.warn("Error creating deposit tx, tradeId={}, attempt={}/{}, error={}", trade.getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage()); trade.getXmrWalletService().handleWalletError(e, sourceConnection); if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while creating deposit tx, tradeId=" + trade.getShortId()); if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/TakerReserveTradeFunds.java b/core/src/main/java/haveno/core/trade/protocol/tasks/TakerReserveTradeFunds.java index 0ac0a540..5ab91343 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/TakerReserveTradeFunds.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/TakerReserveTradeFunds.java @@ -70,7 +70,7 @@ public class TakerReserveTradeFunds extends TradeTask { try { reserveTx = model.getXmrWalletService().createReserveTx(penaltyFee, takerFee, sendAmount, securityDeposit, returnAddress, false, null); } catch (Exception e) { - log.warn("Error creating reserve tx, attempt={}/{}, tradeId={}, error={}", i + 1, TradeProtocol.MAX_ATTEMPTS, trade.getShortId(), e.getMessage()); + log.warn("Error creating reserve tx, tradeId={}, attempt={}/{}, error={}", trade.getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage()); trade.getXmrWalletService().handleWalletError(e, sourceConnection); if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while creating reserve tx, tradeId=" + trade.getShortId()); if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; From af44544e159efd84549f542cfc5cf672cbefbc59 Mon Sep 17 00:00:00 2001 From: woodser Date: Sun, 1 Sep 2024 12:23:24 -0400 Subject: [PATCH 13/18] add donation qr codes with logos --- README.md | 4 ++-- media/donate_bitcoin.png | Bin 0 -> 33151 bytes media/donate_monero.png | Bin 0 -> 28501 bytes media/qrbtc.png | Bin 7900 -> 0 bytes media/qrhaveno.png | Bin 3298 -> 0 bytes 5 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 media/donate_bitcoin.png create mode 100644 media/donate_monero.png delete mode 100644 media/qrbtc.png delete mode 100644 media/qrhaveno.png diff --git a/README.md b/README.md index 483c7e7a..900f9afb 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ To bring Haveno to life, we need resources. If you have the possibility, please `42sjokkT9FmiWPqVzrWPFE5NCJXwt96bkBozHf4vgLR9hXyJDqKHEHKVscAARuD7in5wV1meEcSTJTanCTDzidTe2cFXS1F` - +![Qr code](https://raw.githubusercontent.com/haveno-dex/haveno/master/media/donate_monero.png) If you are using a wallet that supports OpenAlias (like the 'official' CLI and GUI wallets), you can simply put `fund@haveno.exchange` as the "receiver" address. @@ -81,4 +81,4 @@ If you are using a wallet that supports OpenAlias (like the 'official' CLI and G `1AKq3CE1yBAnxGmHXbNFfNYStcByNDc5gQ` - +![Qr code](https://raw.githubusercontent.com/haveno-dex/haveno/master/media/donate_bitcoin.png) diff --git a/media/donate_bitcoin.png b/media/donate_bitcoin.png new file mode 100644 index 0000000000000000000000000000000000000000..ac25c2c2c8aeab93aebdbf1dece6f44e60e1b40a GIT binary patch literal 33151 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Ãv@jbRQJ0 z=d?BKqtm!q!bZ5le9OEqJSX<6`l`BlA6z|Wh}Zm_bWd|HP_jZ&+X>OSRTRqA?n5s7 zxB?HdR=VC7wf;gvJR$V8kW0C}(QHd7@9t|%ijvEt z3pz~L<3+Wv*E_g5jcxUbZRoG@&5RI5T2m2wU%m@7QOAB6@_kx5Q}ujPc{9njgtZI@ zMV9D&qaHdGM_xdGAp!cSW9U|wSjigP|JbndJHibB{& zw?!f)HLen~S83;K9nI!n^y6HQGiRCw7n|9{jTzD+hZn8nU9Ktg%fvzgX<2 zY_FZc#N(DI3ztwW&z)cAv@p_7-0@tP8K0MMY_602BtaFYxjV1oILo`badOh5^VX2= zDJ**1dr@J{WXyg>s9Jb1O@+lQ{M~mfTYnP0|7Y+9ruAK)L~*6+=im^VFU%X4(XjCz z@2pLqh?}0mR@P}UUkhg|wvft3y`X235O+TB(yN+HHz<*po>kScEe{Hmy7$?>Y9d!v z*74!!YK{3qXE@AvIei2NHJ}x%-S^!gle%Dnw?Y5SZI4su80UD!wF2G#>)fM=XoMJ?P(L6^qa|kDDAfqF-+_RN%Yv| zTd1C5MRhZ65$704$d>Y-hI`<*h}*^Xm(Ffe2OIp;RTR=(K;QbDTz* zN*XaQ&)q-TrKUpnOfOgXMmiY-4?ANTBs+leKn&qeR)6b^DGt(9i@AIaTT%asFU0pS#>(h7-9IhWZpr;<+8GTySWGjZL9F*Hw$ z{t-}qGo2xW2>x63sD@988x;oUaza=cWKw8foAww~HHz#8b&ibv!k^XHNaVTt>Gsk& z;XlCqr0Bu_D_iH2T;hWf&&fl7?WOMb6CX_zs-M^lEEZU#leSSKJ_O&EoEM6-u%R}4 z@kD(c=8l5bqQ2~3_*S?dwaLWl>oLNzP-iuQlV$&XAqF*Ou?elAK}D(N+vC1#;|Efw z8wZ>|%J0rk0^=2=n=_7hu(^nMRA4i*X*nW%dpn6A30Y9J=dWH=30&Q<5-Mcn3x6M8 zx~3X3ZcJ`2X7sF@`i`b>H73*NL9-FiY+o=LO)5D6r%A7$8bu!H%qL539}X01SyN7T z$dJ5GLq(L~>vfCMk_O4DB_Y{;Q1Zl7Xx>`X+4kV~F4|jJ*S8e&@mtd;X;=hCjE;Z* zdK{q{oPXHCZ3eI*2uRF& zILd29+;i3U3+eM_;;6av10^X>PnLIboQ~P5{4y!A+K@x0{W4DvA?sZcKFzv~SB6zh z46UpW{<~M66-?UNr<-|}xAl2(p<8b(FV+vgt}TnX4P$hVQ(rbpGs?7bL>J&tooD#$ z5`H|njEP|yx;9NVU3YWx%#ir0-3^`OyK!H3fW)w2V_+G0nh>}{fMO@^T)0+>n?srE z+`aOuZty@fhWMg2A@681Ue#XgzMh=8D(%xQIt`xLdS-NzqFVm>+x{Wm?UD`(#KFpc zrLD@h=YkS@zwNy2%r8&JWRZ0bQ-mJ1L~fL}Qb{ut6t4R!@s#l0`4mzR=Cf$9?tI-# zdgW17kaE&8xc6U4$zhCj`u(VNnXn+EqOVTMm6@G`l#vn%&*Bl=SbQ8J$Rhj5y?){{ z@A|dqegPBeyl2qF8^5p2arHl}y&b6u@+}gKp5#G|juifgcYnyGAe7SW&);Zg&lQws zc&$m#YfpWDrVjj$58NumL&{a@IuLG9tD`IG{>Ifyw%nVA@A4zJRR<@PuGmbxhngJE^Js@~y}cL6 zos#27U%liCF`l<|ndwr@NfsPD)f0aRMFME7-wHpr!qDQDy)Sd|I-R-cz z8TCBrks?7sIht^$`1o6hrmex%G;fP6I{WfNV&}sic{G1To5GVKr?l4LH7kZ)@no!^ zOd6pHFcx&;D{i|gDx9b{4R33EcRjSIQ#XM;5EO7wmUG(Id8^B>_d|Hr2tj?nFWKOt z$5%z`(h|8(1G9p4#Qe7##ziGrnuQWyyxSCBW*68sff0qd zAR&N+o9-@4dH}iWkvy-Fq1|$So03b4>Lq0IfKF@cCB}E~9FmScdb*?1Xs#RP@Heulm#Q#sCqm{A14XD6*hTVwSAe z8iF4f_JIPSoc}j4cEa_#>1R)^C}t4ewz zl5k`bjr?(KG;>do?SGDN}&(uzql#=N@q7)$-1UA^RW4v-|dX!-Kf7iNyZ?;=9${!iLa1$b?V_x zxc~C)XqMHFErUU%D(N{%4aDw1c7^R)6l8(2)DAsq+hOMk`_3m=y^QYsrv$M^i>EtCRgC3<&pID&X58gp=$R+Uf;b4 zVfm=mw&DGm(Q@rr5N^}m7VI?f`h< z!MqG>Q*(2=3Tqj)Zulzecy^`8%#?%TX+sK5TfkG@7eDE21GSV#9mYM=631eES7&A6 z)bD3mq?r}=EST|rx*a}cC~^`f%3q=u5)u+vR%U8CatqV8H$D6+d`joJ#MV5YzRlZM z!*4qO^Z0(W8EKE!gX^7Z&GXaMk25~@r(dLv?1LqZ`T1JYCHVm{2iws-oS&cn*U}s# zYd))h!a;S)<-WC3_`F-=Yb0F>?c8Ovh(%lKQtyDuV zPju30R6o|1`Y-F^D=~RRPf+(%w6f&#u;NoT+^gvkfBbEhH2bQ-n&a8dV!@o)TAeAR zW0dCig#k?hg}pT;=+ZP6q&+k~e*Aac>Xx9d?#!P2!tMRcJdI}Yk&my_4_n5z1yiU* zPmLVXR;C3Z?RSH4#E9r#iK$VNPduAFTkP!hy|bX%^R4n@jpb%IUtsz^moMAw)#Ygp zn?>;yZ558#pJ06YK*94j?|$ddev;^pJ38ehmnrkDpV`@(kJ?7e>ltRxq6fVD=zJ6V zv48l1edya(PJLY9N7vowvC5WIR(DfK^P%gNcb4|S=5->;pl~c{b`;>yyVqsb_kBR5!Rb09dB%Hu367^ zQVw^}(r?p@nsy0Uxs-~Y#3Zl!JefrrFZ9a}mqMPB-N*NPX%Rc2Na4kCiay^sAiQbD zm{oHJw*pS@e@YssmVEKS$M@BCd-Bp`@n=#9&I@q!2YIH{zIGbR<0w3{rm5RPY1CFP zC}LC2JbNjQV*h;>;9~ya1_MFi6I{OqVEvj;M?o6-Pl7JOdQG2moyMiu#z%XN4?C(5 zW-lmU$u$P(b;U&UaQq==NbrXi>>pipY@b@we=dKxX7O6sd{F{7N^2o;%POnP(CoLk zsk0M_cTEi?SFqCF-ywrIk%Cz9$}Y$_8gZjLVwVOAaC-#oanM#hrGb{-Q&65$YJQjRH$iP~sLxCYV ztxAX8C!M0`lFs&`4CVOBk2^o8=i#3k!_nLmXRkuiYmhe}l;4_dO_hCTKIaR7xtq#o zQc&V^Uc_&IUvm7lx0A9yi;jePiJXz?)Kvz+eSLdO8>w zI9ZC=R-|MW-L1drS^k+i>RZ7*jed!!1FhDVk;&79u`MS!7^YGuA!% z!<&mb&o@=zkZsYtXB`6n{wr0{YodtlLr&rE2~A zHJ7J-i44aF?;rWGiWy0XsWTn}VBcIq#Zo;z$uJy1^BgS0{6OXS<ft)6g&h$1gy}-Sr7RTy!h$yj=e?X$i{8O2T3w zy?Kv-4(FsS-SYx^p=`I}_K4QDPS=7==DJmx9CjWT_=xJyS8Z~7)nI0N(*7HcFKV8B zKUmy6>`>@9a|P*B;C?yfU7tyvnoM++ znAF~NAcAHuF4rVtoh|OFswu&g-(?)O)RvfP>;oTr0P+#lEm6}sih0SK`?|sp@3$2KKEu6pOwp6sX~Ivz+mcX!sK%a zV{@%!96w1=Y-)(w_FD2&RFkhSja;pDA{DJXczvG!4WtYnMqj}62x zzU0A&C}ObI$Q07lzr_f%lI4v6~ncddX!%oG;q}fFw+&O%3m}`uVgqax6B|y<;^jy5Z=;H2l@Np0Lj}^INB|(^DHt;Hu+wjdtYXX!SW{-?S3oAhWs9-fo8?v>`8@Q7z-K%Oh zuhQa09brj>TdIOOi9+cv@*xEIW8|VVhOOV6wRey8FF1cQ>XoanGI!dKoMi}pusRx9 zO%O!aiE#9C<3rj2jw@LOb{##B6Yon%H+qK^7?t6=IFhaxAMJ&vloiTZGAY& zJ9!p5y;DX;Es}*zplPhWC8LpRbNMwM*pSsg;3TCEHt)rt$hEzTkBhH92ox!}=46@n zrrFu0&)6lx5P1cnk=nbs(K!<#Fwdi`$xo)6PRH0CH7?8Y@VKCuuzJ4Z)7j_Ni|(#? z5Hn2Lm#&~!JL^vaOon4!4YVhCSU4;7-=BUhs-&IiKCGm^k+ZbI!#|o|6B%@qk!<|C z-fTYQ6iaRoc7q|{x}D%n;{DUC)1?<<@71I&{#en6GV3t#`uyrt(ttj{ zLd@tw6lrlKt@cEhxOVYQ=U^Gyv)cFiffIj__*t>GNMzy0`Jr2G+fV}*zCoRWn<3^7jNXa z&3$c1We~=(N41K)K6`$x^07ZzfFBx*k6fBTve@pQOmA}tz`dvP2F*LRWUjq<%{8=D zhvgEE)~cv@2zCe9Az|R?qMhtM(~L)+S^epY^|kksKQ*%A@34eKCB#&!E*}*dS+Nj5 zBa8KP>gb$X5&WL4M-z`xgcKY!6GCZLt>&-{P24S!xE$7W7=RMR{*Sh5nh8Ijt zuedK%dV6XSi1{E5#`;skXI38ej};0mgL;+*uBnw?b$5%?jggo{+)ezh#}TcphO53g zShg@2+h~?Wo%vcm1t`ixLZ?R0=^7T$JFS?^s)fH+2m8Su_Q-a8aAxB>v82+Kzhu)5 zMV(@c&xBcHr7gTOqyaFv2d)$HxG1cvcI9_9ldYmecMI+B6O*gVwgtXkXxCAjn~WQ1Yr|;Zy7@WW+PxC>9Uda3m$I>m+9P#?KX2rG%(PC@c3pl4 z4@5})P^Kz)cWvf)`q_<#4eQ4(KyqNHv&be2g-)>cdiS4hmsx9@jC)j}hoXi{*@p&e zv&;rNg^Nuxnf2039=biym{4tDUYo5it6P1>1r(9pZBPu16Ut`H>objMQd3@C!CziB z9=R;O%X8NDnz~<;q^_$}raXeH9w0lm#>}=D9GI<{egA86KKgQ!jK^#0CPeQm$N~}| z<(>NaocsD{!&+kJm<^)tvlQXQE|KX}A8C7g=mM+2Oqn~LZGW%%T>bT}7&F3>m_^Eu{i zZ@8BO2TUOq_*`nivY(`dB$=L>kzRPHOzX zaIkY5A_w$oy8-81PWbnCHM>8;Qob!Y=I9wZ*mp=Y7Gsz`vsNL5V{(B!@jRmFGl{g7 zMh#1uuS%Y+_*V*e{oxyLudi05kTB4i*!e?e^gJv>v>z^$5XEcjIv5sS_PSvlB8{#) zLDiDTUASpgjftSE-rCh3_B||+-JX?_z*dVs4+-Z9tNG??3;E#>;`B$4SqaCXiQ5VZ z$w>Z1+0O7nlJr3NaUvl1gY-ZM8|J&ptttbG(z5E)fyfl^D=saE(3Q zmfApJJ;BJoGnJ9ImvXyJpC+<{TvnG}A+sc6^J4$a>zBI|v5o5M#bqyeNAhc8;DNyk zU&rwYJATLKPrZiBV7#KWpXRl_#x&obZibj)ZQd`>8Y#^Ux4q2uH#O6q8Vh40Ro?+74Kn=8SJO&8N&N)OL4;vNEor+Zf2|JuU*h3VM!0RNA2HsnaFqjUq0W4 z3D5ml-^{1&65rUu84P9nB>xlad6^T23!S{7;E`sAI7k{{Mf_ZH9qHMNbibgA^myQM zz*m^5H$?qubNReavnrr}A#044xJo7_e>=>Sx=$jTv25jgEujQY-6Py5z8EK1L%m+; zdn=5z+*Vqj1VI4_KPONHAdE?gJe1e-834Ff*seL1<&(o)jcQc%$B+f;R>NA;;Nqzr~+!m0S_9mQ0kv=*^BF1A0IIrcVu5-8+6z8oWED#12o+uFwvz_Aaxq)nY#il0S5@q=X z+p-q=+&uN}`@kNXuIU%XFU948%NcYBLdh~W!$B+$1)PU%cO#h~!C}B@ip1$OWx|S<+qqKq zKS{I;rn+mXA5A?S%ZpIf5_cD7RvFT0{iBPsP#a$h$(e-6ma^Q+xFf8V5;xb%4&es{ zr#%$00rzff)T373HGQDN!*Y;+%jX(@Z$W3Vu!$kOWO!!MzH03Kx$cWmJ$Fwt>a>EL zZz_2EJGcE_0>W&MhWgU+9pAsN~j60MHkmG{yMH~8U{$#yVY8*X(Y$9bJBzRa(ZQ-bczh?(l+iPS~&{hhmh z3m8WPAbvHsd4zA>r`Ocaq167UvoqLQApm?AOo%~A8v?jl>6F3LBI z@(P#F9W=j@V>&Kir7`n8KJfRR!Sk>kl9}5#B8WjcQ^dvdkoVLI(jr10oFIR3S+KD3 z1Gn0J(mi>`q}Hl`L8PAn^BNQr&zoWb6_#0#xO>I4up(kB>fFtEwr|mlX0;dclo4~M zY=roc-E}nS@#2fi5495cr3^BZ>A2Z-RYcECgQ^PIjiK}bU(on zdjWK&35$D?$>GxcObMF~1((A2M!=0-sBfF^VTH_1n}+N1z4?KkNgiYTAql^lR3-^o zNS=SVnf}xNVKZb22=OP@W@ctTPih|}2_q19>di<0W&{@3)-hiJR<-E*K7B!hZDG<_ z&+Ww&eB3eFYp>K{^mbfaa7%N5BFdAa%I#o>$gc@QkJ|gKsUeCe0tPFr^j61yeO=B+ zQ$NNE0=tCs6rror9O1M%6^VbV@S|wgwcM`Hl@0`|v~TYIYBNjmQ2K2Q*#}L31bpf4 z_w4a4{&U^_TqP-=Fm31QC8LHA&Y2)6jMpkV8U{#r2qqxk(?jpmq+46@J z(kZ0vq=Q;Z1sb?JwXmjSm2HA?^Vr{2*08HZT?YfC1L$!7bSXNi|3 zFi(cqAl)Z56HaI&B61O%pM)#ZL=XuuAB_04_lk{IQXwRm-A+hxJAw4^6b2{pt#q@4 zs^4=CI~o|P-0yaYXJsI$dm>eg@qFW5H6Ra&FeabB{_=6Jq|@pI4v1Bkh*`%rL_-s9 zHC$Tr!gHF0>7+dh*4X3v1{swck4yG7Wu1IH5p%85vkQ86?e2zjhhJOa#v4h|)j80z zIPieLQ|dBp7QT{#2a4>iQQAP;%)lD=1&z5TJ;J%k8O<2Cl;WWpO~K4snX+TIOPEk% z#gk1dl}W<)B$V!*9&RdxLvz7_{rR`w7ayd&7l2eZw~TX@8DET~kY^@l6%0{_*us=0 zK94nvM^#4UKG52TOngs>JEnqkcws>i3Vlq*?@5megFK}GX)Xu1^4)g2-YtrVJQ93f zrqH!7xP4AyUnMEta{CIQ=c#s4iNPC?jc~mx_HNY-ZZGcdKti-*7-;n4#NOMjxS$6f zCf*-PUv||neKaLE*$b{zedZu9>+mw(N=OTtR&o}xheI597*1KA?GttN#}i|~Ia(pT z6w#5yIFu@`4XW~8bFZu3`+w_9)2o24e8Ww;{L1j5GBS%!*XL(YdwI`j+?z;hFX7xH&bUC;bEgiOOqWb! z=7fm(W_yZNkU*{AfdW4{iL!w`Vmyh{!ae`7e9M7l^5tqpX}HzI6%UJwedZL6top_v z&v3reMxlGS`d-k#& znSax(gzowil6XF6+Jr|vV8y{eXFbsgOTda9}z5d)jg0ck_?o=1AJ#@2>BHXWxf?0Y)M)lV? z_}Jss(qzO|dTK+1!A$fR1+4aRTV@rmX)|FUs#FwIbAkOs6P$*RfCtU1Xp@t!&pd=p zMF8QuvkW?msvQ+|gbZ?~+4uR`L#C5X0DwUrtTLvgpVaqte1#~u-GH~V0H<^ExSYog zgJ+i{{*)tKFnG*7`*duUOoP#Ex==y37*QMaD-$tQ3MM(~TCQ|l2xTft!Ja5^8F*GL z0$#+$kjcUN@=saP3I9`okmYKfOjRPHf>&L`-Ma7zZTcwXi>Xf_?v5HyU@+zr zM<|4um{%fF)&E3Kb$!y=Dc-aVl?6TM#Ut$rZ_rA)3~e{gd90_feVZ_sRwE-$H`l@< z70b%5!Ha-*rK!C5#tdCwj2oBTe(6Oy^~7wiyk{aCp5V?iFrw`$F(M-j?bM$_OGK&e@9li$jiEQ=~bVqGpTipi|Zxt7I%bm>cAyqb8pAw4UOY}o`6lo zC3NrsoMw3?7#Ef1@IIJtjPW2>L5&Ot_ZuC133Rk69LQ*fWs%0p=@U+tYbBIrWVbgu zT3D%Fn8ddy0!tULDHRRcls}U@SdEKz3Ri!7wCeOx(h~+0*f<##sl9`f?jz9X56xhajVX`umh|L|$w0<{g`cl{aqqH~X8I-4_;0&xu4#&=n?Lmme zxd4m9)z3Ut4$HiIamZ1`F}*s5;>H?}hVv^k!4Nj$891=_IK8ZqPgE@pZBX}&YHjWGoc?Vrk=5I(QDrC@H?Fkpqi=kzBsIp4jJ7vbR_lf zLJhYvEjWY+)tSwtM}x`DFPI3W<}yIHV7|?GvFYY$p4rL(89rqjiV|W19{+rz(*m>X zwwbN-KACo%J>H&Nwjs7H;N_&C(o zYq3A|mHSbP5|OPxR{H}$HW5FS;ia~-bx2g*WOsh;@r1ffdj*E_8uvpZ5$~&X67H&P z>MkoWyFC`wtp8j`@g)*$dUj`qz#Ry`%RG>nEPppy<9^Wi3GZMd=)kRtSS;(20Hj@A)3oD)HxqGjf~sFl z_AIxZ-I4lh1V`{bVpGI4(`vu*GyX{G@m4U*)48Mb?Rr8(<^-%Kk(WxuD~^qBfhpDTc2f{Q6V!sv1aOKcQF{dqDe>=a&tuU@@MeE!bk zeEsJ^lBLqfU%cAf-UPIj01737c73X=`U5BkZd*VsnT88Uxe6Z-KGs zl-o>{K0D)lWN%7OZ^Sr)uDJlP!&H`X53?XAAGN0Zh#Dwq+g{n2ej;kqFz@B1yT-(R zC_S|fue7QnXFI=3JN@;m`|2idVFD#PG9}|v#im_XC$qk%yYn?33n@#CxPG}y5t_x< z@`f9v!sqfjVfDWL_+A27=%F5J854d<&y$_$8h6KgH^+1xLzzNkXZ|8+Uds}nun<`V z7Gf3inkr)H`RQ5#J@mo>JszLsnenMD#k$C9w3fo!Xlc_~j`4Hy%A<}{fhwybdAWCv3Vm(be zWUAalY3wt{ZQf5;AJtv#ESeS`7*VG&lH?mf;xV1uu%>fzcCp#~NH8#X0>%!@!2|alVE;Y~01rk4f!V{q>PC{LYo;*L zVXgnqx;rt+@v0XWEBXolE<9NtW<4dGT{HD67vR2tW5D0h>=B+8k$?j}0g*{^KZb?# z52ie4jqhM?zE#w=Q-AFPDD4jwn3&{8E&KJfzr-4*9>ML``EwlEWM$a*cfO7RDnVKf zWj(niay}3ab}2Ao`T9I05+QO_;V=!V8~{GsduHOQca!Cy;Co#x3KA=o&F{Aw_D8!K z{HrW>)(Tv3@-wd%s|%Pt=jJ{fDe&QW*5XQC+N}i!E)VOk4Q`^41CbN3rLMPtCXC|S z@iPTZiVFu5UD^J=MOIUC2;zX0NQ3p;)y0YJL}^EALBQ4Z_Vi>lMe7H)IYe@D&g*n! z#OqtMH}6YM5n&T+K6)(IE@jiu`5b@7l=pb~5`UGDzwb5KAKT}REx1JF)}xyxAvZ|x zY2PvQWWm3YFo46Z!Ev7~BR>D&O`EqsL#YIZ3T*tyd)}@gP|4iP)NHkYE64hbq=!Vq zOpElT?p|EGdn{c?e=(2uD+Pb7Ob06bH;B4MiBDZ)^c_@GzZ=JnM|EuF<>YQU(D)!B zur}DSA$xex_lH_Bw0X=OQ7grJEB#UOuab0yg=fj*brS zpb5A9D?X@t$7Z>+57%!{I22PvwyT&9o^F%NJuDc=5bKZ=5F1aB9m}Fg7US&;qY_28 z5G#Hz98ym0orc}OBls@jUA=#EL}idxr!>uXJOR;#H4KJU-|}p2)Qa6Q$+cEA60#T+ zzW=WGy^f@jWb~~xp_@3#7rMSYASNbGMslJCX)T%c#~&S?O8v08kN>88ba#JrH<;U= z-Udx6>f?2dzT%m(To<}?ycDgNEZ{7o8}FLO%`aHGFx60uz{mdU(-^yoORi*pN^10I zDYE);%m2*E+9)5|uc|b1HGPp`8AD$ptKN5)C9nFa*tQh2*!Y)UF$8)pnRrsH5zJj# zSy}Y-^gd@@2BU^QGY_hmK)rIi*sjRAGFpblWO|3NPO=M>XKZ|glo1cP`j5<0N>L)8 z)@(3dli)Me-%u@%p`@S?;w|?n=NRTa(Tb90Jzz#jL`O#}3hjyCB8=62lZ3?Fvpoel z7|oNX9z&;<(9IK2+!{-Gc%;T@E6yD~HeZ`w7#OS_=iK|Uunk*vZYqsQ`&CFu?y`D} ze>AGJ=5oKQ)Ec(VSaTQK z!m0S|elUQOu&x^V9_yKx>K6#v;h|#YuBer@!TGX81nccLk{n65Z+QG_KrnerTJh5} zF!&y(Q`szY4 zNnp7gwQnk&KsRolQ%+CQwU10iuoC?h-1w@-qIhTCiUi9X)5ofz$9skSF4_XNWxo3z z48kLIWLh9Px#N|E=oK3`>5)vV4Ot^B#)twDJUqOIww0FeE!-368<}C&t9cbOo#6rg zB}0SmkG47(QiR<1O-AStaAxU2+Qi5D)Ac)F%t|c7@w34JE7fN@Mewo>GDQ>kn|~)0 zp~>IyC4_bpep1s+PUsrUs9Kp=*^rkWp}djJ1zfCLz7;$=u^B~cf8brVA0Vqz_OADW=5Ni_oodshE5hYwn{I+nbkU}SgSzeaNAW| zS-32FSkgKOtdvKo*ftV+TwZw zr%>YN=qHpHZ!PTa9pu<%15%pi>GJImsQH7$Cy{N6Z@J#ckcg!0vP-G%o`BfF{wA4q zfn}{{Ze-Snt&&4D?GfEXDxGu`D2O~{p$;Nis2bPhh7UK6yyZ-2a`R(NJLXQW*TAXw z8g!CrxP49-E49`4Ky`J03Dx0!H82ElI2#%bcpd9ijm}5UUZ#w;_PuoDmW|0cyTKwe*>^GHP-RT6iVb*;$=^QH78KoYRVQ8|#EFGO1InIQFLiJT= znXo^Drj{|rRIB_f`OyD5$=j)Vqj(8mLnbOAS*7b?mWqlBh7e;+L6@S_-#_?R1F-J? zG;D%3tRw@*{pzfSTUL905Y{m=dncVwEKizQ?C6}eey6+l+IhY<+A>|~Z2zhJ5Uo5e z?=;uon8xJ0n5A$*mOS;%^2zr)KNby>Dki7|oU-cz8l!0Chs;X&k;~{4@{lO*K&6N> z?N8Rv!}6zf-$_I6gFG0$_WZWgzVNF8y}Gq!!$+0B+Ses(b?NRKMGKGv$a-yaQzvlI z@GPxn#EMeH>-2B@heG`m?KK9Sf z&f4H|(~P(rUGo2{4Ac#AhNot7^m5{d*HnZ{-Pz2?NjEWB&T`IX^!p71eXfyf2Om z94L_qE`hlVC<_q|j=fH%f>Q0gr7l+tmDd#GpdcaIzq_AItbMaGkmFray?7fq;T9g0=&+!6L3SKC5}hXNHB0__xKn zw~8QdbT8aAR~2Me4GTZv5hnB5r!6RB-{S&H@M8J981)tB)(%Bo?x|k@VdI7nh8RQ( z1MBktE*k~4L;J04oVb2XTSZPPr%ZhKv)bBdCQ{OYljxXP1d(1?U^T^`vO`XP)4X)l zl*eY?nacKo4&ZvS2M-1gH3L4VqdOU?iSh()M}yN<)%%`13=9l}P@&TtgR2dghCY!7 zBo1?s=->$(yb5`u3+@ED$BOxaty{5}Z_xS-m=^+RqQiXi*8G0A=MxdF z7&X0{8`-=9`UzQT53%NQirTPSe)7iXp6&C>56xsN^bu=75JurRfqmvF-p_e?$H=V< zs;a6GAq^=5R%KxO2f^AZ)DHOFhTzT}RlR$D1>g{*QIx4xLY8gK-Y>^KVb}R4|JO7- zp$pvd;ZR|Zy@gf;ep75hQj)O4QhOVfFU18^HpcYEjT>N6Z~|r_`}V5NK@e703wDb+ zm;~7ZI28C^Z6feF2`9{(5v;d-C#I&RP(CL}f>*##EEKM#c9(js+w+(6%mB0XiniV`b|uLoC~8AJU>5=YPSsv zTf3WHo>uqzY;6sNnyqqN4dtDQ6W6EwkxWbag!9b9+RknleSw-SwTveGni}F2ZUI{4 zlNnE2UteDk)u-UZMcOyhaS{#PBGARt$;Qw|+Lkn6cVHyXa#t*Byu{qgOQ@(3yG7av zYazOz8hq|fr#|VS(h;k7pt}~FJ?q`wE#$y%0oh;#3y29N=iLDxv_6=5h82gv$9d5n z(2P*afdWr-vDNG9Vx`d+K?abEVS+nVtI2hBBG#PknU(_V>~BW6{rbS z`VtV}ikk6mz6*pY#jSSW4Y%v76T-W9Gc6grZzx4;`c*E=7L{3#`Jg;!b&xLaaz-44 zz9v|gX>X1dT%%9w>phOFRvZ1GVE1OA>!1U)-mcpdWflX}_Wlz)OrFp z{=)7j2VV9#V>vy8Ur`FW9G;w9yovYdv^5w9k*KIU$}=m8cZ1;f6N7^Ma4FT)#%2o) zB98|+%RsE}@=opczak6`3lkv3J-icyvqt1@@tBIr_i#Y6t*uQbPkq1bhVdJ`vu&nu zq5XE?kQS03v4E^m?e)a#)tmXAYpDUhe*J18v7){%4+67~?P;Xz!Aeh1^II|7NKHTe zq1jnq5SFcpGLQ!Bvu^Ne@j}1-?Qh`u4LqpZTt0GV1BEK67Ka?j8DV+IK$XDvryXyN zAAo@+&8#z#_L`cz?)v8D#R_lzS;Gy&JA{OSz)~o%l*e6MtH`Jo_@MyIE?fS1?4dDJ zVOu%>rPlL=Rj*oA7t1561^e)aP^r!D!ug^$s7}3)H~N)gOh#;};DMTk-cdVE_}B38 z5GJt#M62N+Ty78j1;x(t5ZNz+j5gX+vQ91!`cSj=*I?|^V=N~gdS@Cm277yZ_172m z^;gG}hoz5 zA;2BcqLVW#fA&v;7{)FWnYy{1HsVoT_PW*$**qs+RSwim$O7B}E_9`GoBQzL1C~9` zn)`kH{>A-%&gaj|K{_7e4Z3ItD$O{9Pu&3%-vBBfw}CW?h1+lbPj6Qq)#RPEwVk3B zfuceb1&$zUWwRPYLNT@ks3Iao$O0iEDghDM0i`U;b!$&H%lY2?f0(FkDtR9Ilq8=fk*H$9TjF9NAgj=T zBqg4n=7*CT5VW_on|6nnG)KMxkHUYQP+7a9i?G=Dhr^3~eIXHPk`h{EyMuW#nypD# zYA30?vSAN&#u;?r(BTawxaI==9K89lA_>s z;8Ot}LXWNfiEz8NKfPs)xa#d9h%!?t%RDujG8vJRb@uQAe0)<$Ni+ZTqAzLcLNSq3NV;kaYK|ZaMw^ zCuQB%=fn#}OZsFJ@igh1qE;@KJ7==7vC|@3X~~i?2>aOSvB*5v0wKG%%bs#AtP;th zUZeM3QfB1Ft+2;6#}hI4e{t=&In~v12|IXJZP#prBUN-#K67&W_U)tTb|Fr5LU~c_ zg$q#6BN;I|gkSvdW7GeqlgE_`+2PZUzZbz~_P=VST{f8G$8A*hjyY(D$7bi`q*LdH zY2sv>c(y-2a)7(|@TH^OB0<%(&K*a4@pmORobo@J1yI^XH= zmkcqRL4We(yw>Hk8z|A2F8 zdk(Iw&Khah+mf7i1B>3o40~OzcPI1V3e8C`gnB`NhS}yy6y^2tdIrL@YW5XFKCUL; zYG!r?P1?q^w|4Jo-TJ&VuwRWfTcZ#Iy8UJXl1KOvEewxTtfkFVoI4Ifxr~dzh!NYE zSR)dKa)FHVxnbI9-{o8E5jk3H9is|x3}9U>M>5x2KkE5>J#DCw(n)*=u=6aSMkRd7 z$V-3C_GJxIvjXJ)1A1Dn(R+-ZV=n9J5n2?H)z@;>8f5esH0$z3iRseU<1cFlThmLr ztH1RVMg zVA-S2%evIrm+g+|`R47d`y3tZkRD4(Nk_fBydnW&io9F9x^NDn0F1q1yB9)h(`I#* zyy^`}{(6mS>IDOe3{h7xjBU5^=s-HOrddgJ6^Njj$iMS4%DM%6TD12Z*T9!+&a~lk0KA!X!7Y!lKiZ$ z^hM8c)+jUzNB2EU0^yoYdE3tK&XI8%8`&W%ov3uO841GiTLkPcABD+3$1$A>36d81 z7P=r8p!klmO%$Qv$#XpyXzJ2!K)WoZZ&@_6IYj(ZKpN8>qac44AkbJ&RKHuT&I4{IEezGcjthW-jD8pl8j?5J5pwq%Zm~<&_l5;J`q?2g*WKrub3C~VffIde?I>m)H z604%bC7k(DVtFWkasw`BVbgMc3f17IqiKL$iV6+mNQjaNtvv^8&VO;`D4s;>1NI?* zWh#4>#wmb|{FG$cR9aML?n{{sC zA{)RCZg=aAnrum*f8!marYi#xIU!Epdg=n#>rhy4_#ixSxo6GKzyA7NSV4&T2MjFd zE~A2e!>XDSI@0FaVg3-rJj_2DX)W0T9QQ=`J;6Vh=#);>t+lhWo763ROI7MiHc46~zJTmej&;A338s&1IZ8I~Qeg@+g2<<00x`-&(5Bb{8*NqtjEl{GR#(vU3r=Qvx}$322F`TG_M$S_bq6-8&|TqC0G6S9YE z83>x>X`c?%Kr%;qluENNuFVW?I)3QTp{S@Rkh;R21=gE&x&vFalsD0phzEpi{^~73 z>c$PiyB@|yOs3{Wb_a1^YOxYbnW^70O-gQ}T2~QE175b6_elYFS^9Qy6(9)(2M6<$ z3b>+&kNf&KurYIk$rzNcr=@{jMcpAvepMYAi}e>Wxo};oT51&3xS~f7rqifFK|v7_ zJ;o*mLlt-fhjuob4P1{pdA+YfHaCJTAP534P8O;fui+UXcnSHcBm8Q;Exw*B?4Q^E zAvXlG?q@-=QA10|hj^K$730+k9FWcS12Vj(ZK!Nc=3Y*a9|;HufMMRP#J_TqYgqzE zJdQLZ0Dlo1_C$f<1`8X0B-I#%SV+L$Q~j~vj&)WMQ?nEO-mPh)(sp{`DKg3p7Fs+W z5A267OaZsUn>S-)V}5R<-;N2}ZY}6>>=mOXkqady8|WvDE!+_nM+QxIeg=BU3t)nh zH`~u;U5#=!I9Unlam5e|_m~EvUIC-EE6XFbFaNQ>_>S_^B4OJv3*V@q)vRM`YU;Lg zj^armAE&$6UhVDeaO2UTS5@PtbMDt)pWyrLsIRM297|EON2sU%{(hhoLaDJzX`8UE zdck`j&a@hr4-O1KuMeA>8df9^L=yzAbl93xGwr=Y&gG(;%T1MF8cg(%6k0C<&g2DsnQ#L)) z8GqqIX;u}1LLR3ddq7V*Lcy^#HJzN46LCV5Y%byK2rg#7j#IL-tGCgfgd-6Va}aXa zVKiqpf&ZX!_xTp1-nm7XRYtvahbb`D)(eJ15l9vyP!R#5;Q#=U!gm?XB>6kvcGuQI zFZeW{b#^C+tpva`0gm6n8gyRLCzru36?_UN5pXHIP+>Z|-Z3;*RbDjJv~={3wasL) zSlxbw_@ZM_?lnKFVBwZ!lo8iLcMBx4L#$D^=iv0H3#IfQ3kQv1YMTrNa|2D2B|7f1 zpa?qvo)tNwsH@EQT`AYDJqUPNonB#Jb^oaas%f&;bTt{N1wac2UqB3TgN~T(aJ}%s zKngrIh$Jd1D#f~zF3lgZeh>y4|8FQeW<32X{C2>A;x__*hcy!ul3!O;0oqdWlE9?B z4o#H;BwvTX2EKZzpvu61hjY>gNIR3F#C7&x?ipb8X^z)c-}33{>qBqbc8|&YD!w#E@&E1NK;cGgMDrA2L`m(^osfZV9WJ;?67|F;}YGfQyUAm8&PEQ zVzjA81u)fGs2~EoQ3NL+#|8ufrURV?1fYR+Ce&x^*jjH^|PNU4ZLg7U+H~99vH=2RU%LiV94e7a*Lfs-d!$RI~HB za_1O^#)61!HQdz0sl`0d51>1nttyy7lgfSC&jj*6O^DgNFOXl-ON+0W0fA;G{9e0iVj zB;l;n(cwCV&v$>eG8N@s`kw`@iNWYwFzdeFmlvwe6RyubXO^aVpo-p$C)S(n)4puP zyq$R$)xo}7mo9e%ZF&ow`1$LC_FvS$eUsz~_#_QUEz)7IWCP7rY!jOs*och*bo)Hl z3nA!aOB=Q@!_D+4V+^5^99!{A3B?S$R!)?F<95XqhI~4qI4WE9u;(W;FAp!X&-T%3GCYRg z!7;$^6qNX4Vx6n#TAP?@&a&MmoWNyy*2!S4ZAZiK_pNw3HovAq%Sl_=kj8~g!+&ff zKEfY=Twr|k<$NQ#L6MQ6+{Wutwhx-v?&?8J zF8b7IIK%J(3mMUj#_u;rK_zflkJmP)2Mf10*&sj1+)}mLq}Ll#M$d(R=IaS9X8zh% z@4ub0;1ckUm#4TOUKqkE?=^N``~5CcTVQ;REHBS!hL+Qk+_R|Vc_MyB9A$O9z|mFk z?goA1Yd}S9)NZk!FQ49}_@?mR7mI)V%#YYZ{>97p78&K0EysNA0DmHaqb3`2a@8y0 zxCF7jTCZ$fw9S;}ff&`VoRS&2gO^#VWAV{89l`GPCXKAjPhBfw)5hcm@Fw8R=c`Nr z7gqweRY-|&vV-S@!xRv3uyG00e5KK1p*(Tk)(#;s-|zWp!=1NATf3la>3US;j33`- zcG*4f?b75e4($mvc0m6s=r)a}z8Ds6iMrW-C>+&HeGq)}1Du*zUSimYVuGR4_4U3n zNCL(O6yuJ)Iui!sdJO{ngwqNO4W+*w;Pdd5#f`9R;oFoRRP3_<^#xWY*vxTJ<{ZYH zE)H-SY1Q;?Dnlr9;N4y8p(BprsBA*FK|De3N(?$-P8 zopbLw_g#w_7K=Zg=iPh%YDZ{2Q@%q;OL*(ntvf1D6|}+k=FJa2Ciuj&JAvN11^K3; zAgAj!xt-ywt2;K*yn7p~jY$>zVWd$=*N`WqlWZW@f%#L(IPGw*hQe_BsKq7`2fLiU z+_${JVdXo;o$*QhG1-;WLbOhMpK$3CbL70En0CXm+pHj-jWPxtbMCuDZ&&+nJsEG3 zK}>E-pQiinG9A^OuAKs|pAM14B(z0-GkH0$y5Ly~1Ott9uV} zxG_}VWbFmhFQv>F$yt19q)1Y6mlYpj0c9d$?V=jsI9%=hBI06p`d64Xh92d<`!h!A zn>y>|pQaf?i?+K*9|&9j!bTjHmX-{0b&QQ+LmTgTp!-TYb4~q+^(vLmzbT1{i%XyU z=w2ZpAZV?d7wwy!ZA2i*Dj1}EDjFIZDwGj#*ZOv<=K9s2JZbxLeHk?A5^zXIOia9B z;guN|7bik}TfTCoJH^x2DkN?ril?!wuP<4+{_M=l*VlKic>fS*)iSx)q~^^+>j$D@ zte2;X4_R4hFR5dc+Sa4OBO_ndN<24j-w3#pR@CRn4n5?y*iJx2MfFvoNOyI06|-_{ zYb%CMM0IoVXk+O7{5;M-X88cr9HX+Tsw(hmiO~X=lH25L@^vY}hsQ~(2ff0=!f6Xv zKJ5Ua-_gfudunJbj8^HUE07WC2gt)Kto*hx+x5qp|T9YdS612&EA( zoH_hH@-C|jtz08MwCtcdUXL`S*K)L4dq3D)L($wsL`0;%8#J;_#1?5o_A+D6PSes7 z4BY4$J0JD!oHSXDdwJUELrKVeJJ3RbPghII+U;;Z!i}plWddiHgV8^Krir~}t%(8y zu<EG#&;QgnzlD6+A=dDylkzK?CnQ6+n2LnOW4<8daS z_x2#te)xjknJ5S9%(dO=+Tf*2gUP{Xu}qG$Q1ln2+s;I+;X#x6Lyw`<>PJZ;z4>~i zJWW>z0{xO!+VF4RmPX;m^W3xEYZ)(KdITSwot9$&+AfqGwNU9^Rr+9qazY zM>^_NF_DoMdu_yMDmjvhga_f$2#bS!(uznX{0m(2kXvg0q+rfDxVVx=N8@?1DveC_ z_1O{uoGl1Klt54Kna)d!;o;#G>lqrJ7sy-_)c@wtFTwV9= z27#v~FLm>XXzbzfamO`40dpfNDJf;NFz?Iu@IOe4ffpflb#==oB5G$Bo27rR)?|oS zTwPt`xyI3GGzx_}B2G4m9dUU5I`DF84s(GgV4ukGx@H@+#G9$g*JDs0r|9)}KgP!m zNG1|Ke~!3xcXzK`L~0f3aw3&spb2}R1W-RxY&r1z$Et*(jd$DI+y3Hdjg0|3b!R0d z+)TQUlbGt{jzk}1uC=wb1?3`<JU69~^;Gy691;*L*G@mc|lnA8+9QVTiU z+g=Vho0r1cX6^zf;!Fi#dV{HDv{Dz$vjF$Az}2gG73N9SZ6*+}zJC2GAF)1n!KRTG z5|YaXVNE?Qn{+uYlG=hRq7XXw{2ro{DQpEsQllxwx~OluHFOH0c23; zARs{4O;pqa<61^4gH0rtot?c|pv@~YogJKX-T{0VY%ik|YUW~N?qznu*1^EvW z(DCu{_XvmXYt(sc(~7y}YAyc$9clID;NalT#aI)RyYb>TN4Cew*2K<}%@6XOM~KMC z$lTnAOeVE%uXJ=0v~q?v>Khv5j;05`DFN{ zJt~=XbPv5l2f*C0-ahr$FD=RE`lV)-JNV@y8b>|oe*=B_oS=X}LI)R)S*SAti7aQu zXIT;D789d^Ef~2C6=<$`Lkbskn8jl2Ja-}9L-VSIqSvb_8>mKGxn zecX;tV1v`a5pNdclLN|m8F$V5XyqQ8s7%WDqbBahC{pkON}Zc{YZmxe-;XG zbbsVb&CZ~|Q7-MLMD*p|T(7*pagYOoQTSFeKTsQF%Ek85>XBLH_XNO-6l@faE! zie}vwupfLl5FpgTHV&DML% zI{h9ls(}Xw2UBF7U0NeWwT)8b~vKnv#(mvx4`l?kM6 zUJ{k3n_KH~oubD5M~^m$1J8oBv~_fXgL6lV4YB&%mqd3aD__gH%{K>ibvh4G!4I&-fDoY$WxJ?H#S7o&JEEsc%oMokdb1YQ(bzB^*+kL(zT{>=UTgZGdr zZNNl!*gVb!qB?TVDK#sr-%SW~LubImuKWScm$Wp%_?(IL`<-+K8n6+}O7P;gij&aR zvT)y%H}6&g{~R)A+wf8si?WGl+>KL6{QKv$NfxR$J|zCXbq9)-FL%S0tr}>mwX8rh zc6(>%*&ps4T2qbw5_`A0R@{{006Y*x9d?0M-QDc|`BT<-z#0J*=)w~iIXQWEcelog z@rxJ7dkas>7Rb3!F^?WUc5rkQED{DfM~m`lVp>|-RJ9A%*RU`=_WO>uwqYaU;^Obq z2JCc;bY**6S{A;=Nn;Z4pxLslYbQ=lJWOWMy}iBhgsAtkHW3Y&Ct%Rtyj5fNrQQhZ z%1lhWmno3sNY4^0>c zJk3B7D`44*J-}SUIo%Y3d|8;-f?O=c3vbGWkHpahaC_O*7jg1Ya7tg+GfpRkxj*N~jPWUdxh`bN{ z4JO0h-hL1hB`GPnce`wuc{2hi6d+vm*otf2bSr{T-rb#f@mXh`Ajod_Z#BC?}^`cDvv z$#(x=_py(KD*moxbH=>vb`CVMhO<5(MbI%^Dgzkv^$$F3ded-(XL7{UJUl$S{b%3Nz1iB@_5LF&Q$ z&sS1fxW-0CEZ0JpX)KiUw1fESYHN=RosN;)-kDkA?hfWn`O%H|YL6|f4ER;J3NFWM zQ6AHZiUt~j2s}3PSJu`~OC}x|Vbn1e78ldccR}p!e<)}+ecfvMKH6IYxd+4N7lL}Y zVgJiawQDKp>6&awxHA6vydo1Qx5VUcst)twqXkI6ND-M*iz9k#1KL;7NRa zCB~^(Sy=-PdqqYOO&VW>gW~1sQN0-wRQg6_c{RD#BSLxB5`_9A0U84yC^RQOad8^RnnV*f3C*G7mLg0R~}b z0*SKrC4s!61jVr|s;%(af4OOs%6Fqp^!4?>wognL3L4@gfzJxYBPL{_;x=g(s%vd+ zrRM2^nw9>Y*$u#RdAXV2~y+AJT2@#t-u?|qkDvu{h{o!v9VrWa{-sz9Z^&t zfEVUAu{F-q7L$;``dV7bQ$a>=S z0btI)RKB*UsVVEJzP@ME$@?LQ{CrlDIF^)dU-hI-G*u8WuT^_*Z%nz3rkfkT9UTPH zS3`iqQXm>TRc;k#Z63m2gIUF%JiJxT4TM(w178Kw-h?4L!NI<&EJ1s+rHPZ&oigb| zKhpTOGY?UWX&u>@Z=c9mbX@HrcQRocGzpBZT+>!#5INg3FE6jMA9$QS32g@2 z7{=cQ=823%5Md1IY;2J%Yv_oc6ToN zvWkE3h#^N=I2`u~C2P<4=x&E1ZTm65uDg2B#;o>y7LZr*@)F&Fk^)uG z6g@sQwRGT+!$^lV4ex_&66cx3hJ|6hnw*+y@pGeR`%TfI2`}Ii2#}@*!~_5Sown=+ zHclXEurGsTSvLa|C?viLu2f)n@4W03ZvX)B?GMjGmP;`3HYkB zQ9u(4$XkYchlg0mg0W2z$8je3N#e7DoBW^^BrD~)kgDoxTFUgB zI}?+W5t2I~H2@qVK9%_&_P7y(GWb0gQ=g9ymz82@-S-z2lNAL0Qq#4I+VlKgJ2-%w zXqagTDlG$3V;c}4O#%!wbrxG#T`HdyE_HN^MY99I6_iz@*FN)J`yEDUdKk#S;#Nkf zK?(pGLDmcO*6ON_1>?fPLUYvG)>d?w{)-nK{E{F*NXG2p{~W!qNw|8w(vp9$tbkED z!rA^C^sin<>kIV7f~1%SXOZ}a@`swS_4fY-n(~Bz@j7D}HkX#LLO@YEIyx+1AZCzr zY%<|S%|`6p$wCJl`$$ptr3pplrP-RAGQ_fzdB?4|(Q~$BN@DdztZ_1!3 zymnsN^oLfxC@YImbRu%iyTL3D#Yvb+8NEE6u+uVM>-Of&t?LuLj}bFepFVzkKQcHp zqA6x_^iAcrb!c@kx(@~+L+Eebi%rNdW0YH z^76(?OtG9?5tCpn_Epw^o%H#8eX4P{w4vb|cWOV)f}00)go4RQjq=Iu_*v;X7RG&k}Z`?PjHr5$fss z^uRR&m_xVZZwFwy_OZSf78Vv2k=_`(f@c{HMI!JzyLc6^%q(gD9~EqmeKOPStcg$Z z%h%AB)Zir)z32L^feg9PcmXtr%f*n1;Qj=1MYx`hHSvw^zj4;a)i-GdxbVc^97`3G zsBMdxrg?+wYuozlm_Sd3u&@se(;sqpAXl|-*JM*Xcc#zLngK5nPG)8hk8~V7JS&-C zBX|i6My@JrW+b8AO=~R>r$k-6F4UXh;*qqkS*AOgeVyS%x@{_P%JW zafKaecZH~!nB_vy}L^qyNJtd;+1N+UIy^X@g71al48+zBaf8EVb4B< zhGJGe3A?MZBf`dJD=S?B{#EcqYSnwljzRq+50nYF?IxZYfZ9{YjWbC(qp>hCo1N{q z#>?aDooFp`f1db)= zE}@^?8#4-&D{Txej4x+wb02Qn;BySbl)Q?T)~~Dc!#w|7BV*&C!9jz@_g-X)sw{oc z;ObcSRo!aqQQ8Yq=gf}uou>=HAH~--Q^GnF|EWOi``OAalX+?>j=AiiSy^*Bno_T{x3{b3%qkryNI)`g*$bKxxtxH5ja`<= zJ-YGkOmZhao{Y(`%8^Dia`yN7@dd~T=A*lNdtU(Ok-%qNP*HKu4h+i;y9{5F&FI{y zKfEbvF!8o7!24OO?sMTg+syYRScUK*ZanV5Wt%opH1b?htn+Z0Is!z_JvTama}CX> zbOP-8VwKH{jGPz$xYU2=K%8EC7J849obJ}}l$BiCW#ECWSU?Km-5@WjQ% zg%PAkQLD2Jz99ApM$IY`$&=ot7rjM>6fGZI2C&(5wZhYK6`4;w1Cr0_@R0{B!w$#) z5HJ;0igFwzXuv?*5`h`yk$S>b*jq`nd2$;lo2uv@FClFjb zy~o|I(<$Zw=$tBRG@0ac&^6OWJdLK2Z`eY6#8VN`;Vn)rM|W7)>C{Q|ItSCFVq#*l zn{P{4bw+=xzYqKV9rHdX=)la(O#e%;CAd7@6**_6i&64}rs}}}@gf#2fC>OYyd~fs zEQVf;tk9L-^Y`$hvLOd2CoUeIloJ8p-B~HlXdp$s>2HXp^^s2gd%oaBl{ibwUPz@8 z*EtPfHz+LPhrs211RD!WUTK@NLWXm)mX;QiH?OE>-|z94y$~q%^uORFJP7%3I013* zI1>;*j#qSkKmj*hZ@b8f+V;_d>M&h(oBwG&tk)WxoQ#}$srz_jFi*`8_w8yA&AcDU zu~>dyUT-QN7Cr!4BL0+@R#quI=CM*@oM8>X5NI4M*f9oP?IZi8Uu(k=h=UP*bD^Tn z!>fX<079Dp5asqu9;(Y2mc=9h(~*>fbCVv8LxZ8v_sb{SQ>3J%tabQz?!2m)FKxVV z*qv=)9m(|h{m7(^VJw19!h=)qa`nqiuUa9ZdV18+lv_(n!IvGpoxC(LovEqR^Y`@s zo!{e6qDgP|7+`;aea?(40K$5ii-EX3<1pgtHf0I4HGlm~5zwD-xRAHdb9#FEW*m3% z8IVz+20hxtNweXAO0S_B4Tkdz$05rTOWu%*kFV!;g# z;*%#M4wV{8SmEZ(;!x5!x$I~P$cBT&dL2dKX*ueXw0N`f_?M=q79|3!56fVK-%)#~ zrzwE7&~mP$d6ktER4wqr!dvm64cj|AF&8oJ|?;RDyiK;w~zhCKxXv zD~p)$-O{J<#m&vS*JIqjI?62D%Dyr2^8Wh4V#Nz~b-1DT6VTwa7{fPPiD6*2r4I^4 zF4i1pdO>amKB;wKWQw+GH}{nd^&|L47?K_x6gmCuSE z9w#81YOy#tJmjin>e3qAJO&xV+?=T=mFMp4tJ=vV4*eftY|E8nW^Io&i;9Yvc8crk z>GjKj*aLMYXS}543p4XOTL^0lBc_sO)zs_X68((@wC&5E)udB96L#e*?BNUqbs6HrLn7c5e76sl!&+z#x2zUoV5~ zINx%-KcVd*8{1M906rXte-hlxK2RrM!NB76CNl8fh}zrVFaOk=Zm0JlB_*X|A>u*> zboAcH8;aMT)vFvv9&=oD(6qg#4r7b__U%Epg15JKM4zOZ@xZ~brsO`h9fuE6HIJ;2 zCPwK)Yx;X6SeDMAGWfCi4A{9Jef|IN{;w=j<~gqoDOt!J?dQ+;1H*V{O18ahAYf~K OOGWXSLW#Tu^nU?5BS)N~a9*+0r;G#X#O%;#n>OR<-+dGG(Y?wAg?Y zKE2VLcy>7Nm*DLpT>d{2KXYa$U^T}CXZeOto0(B3I+J_bLZ0Zq*SGa>?&J$(&x}rE z^`=kIgFwRX40PxSO-Br7;GQsERAVW_6QKx2`Bn&sIRV`;%Lme$MRm?r^9tSjON<*D4Bz<2Fdlu8UP;|vyLt#E> z39`uwf5`rPEBt-u+HoS;(xqE=9|!6N$EE3hDn=z0RyB^Xmv0H4+gnW_qvx7LLwE>Y zES+eQv2>Ze=EG_`l8la7;X)CNh|E=tE?`~N*f(Mst6s#I%qq6e{rdPSjx_x(rh^IY z6hhgJB;Tz$F!f`j^V0I4TzZtehKl!EQ8iFt(3*$Ru$@+_k-^MiUlO4-8D#wyc3{RO;j+U+@~zw_?&z3IKgE1?x7#n*UJ z!T%Cz7O67;*12p#qWdoKn!p%=5@wl7FA21AM8kf;R3u-tZTh0EpoPL59OFG?LPKk} z3FaA+%wHCkPb}ub)Q-PWdCh|W!8+7ucno(=nU}9z1<+|soQ49TE2M(zNHWtNC8R)Q zmg*B`sX>AVmDBs1Bgu0U_VQsCOr0lFnWYaST1AVl0J$Lpr?riU2p41~Ld88a%eNwI+};v@gbnS#Z};;nGfJKK6$tl<@n z_A~H$D^f0=(Z7yoDkA^M-&OBG#M&N&%e%c;@WLw5P`k%ssmOOY+KBu~d7srt)3*=I zUzEb=`IFqA#8=h?Q)ed-2(MPPhxj%dfj3;O*vvEB5+~;;mv-ItEj8K6(QFp{Tq}gX zM}$034Fv<*jU#tA|XeqBcKqb)?)4C zf%B)w!=EFCtxI?^V&qowAtG0_xabGcFNM-viN#Wb(T1g4k5MdFsHz>zE4Gfm5(>`| zlmp;Oqt8OUkby0bK_nF94J5iXaeM?f|_dZ0YLVR={(XSy1Q zsrvJO(ItWC##EkHQr1qrB*9rhCryor<&EcJM)1_ZwG>TSn2z}W$<5yM&~=4lsfw_u zxj^btugd@cHj_Xl0sQzjO85dLgm#nn2`t@F7B!TWdr}}eQ6-wyRKjdEvp#Yv0}xU*#E5e8c5wRh|ncnWE?~vsBkZ%>1~BZiYn2_%d>tOUvcdQ@V>V zaxj9wEK!hn8SS8#Afw#w39i99Hi%OwWEoEs)2*IC3g4y?0CL@!Zo!T;FF~S&M`jEe zFc*{34lWm-rDIs|#iT2+c=;m?;-9TamDGMMPSs+I1f0S>47+6L80T zkqEsv| zO%XNjJBl7jS?Iq1fsx_mki@`i#gO@`vO{wbeUb!7=y9(IqpNhT6iDtb=Ya*E=1vkc za~>GtPta^I;IcLPTszMQZK>ElYWA1BZ8F6iwW1<8(uZ8AYm-?7@;$Y^eDAVH;vpWd zLTs2{hJxsFsI*m2Aj{fqNqTWzzH@XCH@rK7=%m`QS7LqZ`0!*)F!Dy(#7D)^F%6(t zEm1}wSMC9dE9$y59*1f41|y1dAX9KFHLpOK`F)L+7Rj+|$5X%Q*;6;LOQBOvRpqSM zB*66^iD~hC`Dtxg(BDd04#d1ItC(oxaFx5Jf;W9Kz+AQmnwJBLAyOaRCfosc(H zdtH2XHD|sE6^+2p-83Iz)DgN=rKYN!f29a==m(U6`q+hT+-~m0>3iWM?cNX8`jvRcsVbjextGM4h^7U^y3AL*8tW7sk;fSR*qQK z^9`TS%+k$oxT19F-)kbt@0smLp||^Wmhq)*>BO215(a2J3!Oc{s22^Fa?xE43*e!Z z2;+AjjwW;`yae>?CSsYlH}Lat)|WdKcNF*e8pl4_;x4D~oyD7N^XuH}J|`&rcx*F> zt2pZA{*h~Je9W^-aTXqHY%4lgpBi`YSdYA`G9=ojjG@oke=|xFgNKHZsJrCFV@l1{1C#nVka`Nq{Ec^mE9=-s+2-9UxuKM_mhI{?P5XXsflPHC6 z5~+EHr}~SwsrqMuH!9cZ!WjKc0HdD@&`%kC)_56J+Yb`HGB`s=Q506=C1|NNm%d+Y zo?Tyo^rz<_Xs)>k+E(IlKLdZEhhbRVYj+pZVdI_OVI#RFNKU{y`@~}D{vY@4!D%Jd z8*=j}19ejy##$haALe+jUEb8NEd?71s%$?ONZ{9#E1v-AuYOq8_^ZOtcjpf6yT9zu z`JcmmSop1;OYLR5^H+{$q1}ilXy)o|j)*TWv~}BbI8l;j!bhWD3{R||UhlMT&{Ucs w9`*R$L646?mkc6UEm#Ma3t(Zt2+WoxtJ`Z{9dkGZ-CZn#0&xB{z9%pI8~Y94MF0Q* From 30f0cf273ca2dea3f266169c910f587c2e7f221e Mon Sep 17 00:00:00 2001 From: woodser Date: Sun, 1 Sep 2024 12:33:17 -0400 Subject: [PATCH 14/18] resize donation qr codes --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 900f9afb..465e04f9 100644 --- a/README.md +++ b/README.md @@ -71,14 +71,16 @@ To bring Haveno to life, we need resources. If you have the possibility, please ### Monero -`42sjokkT9FmiWPqVzrWPFE5NCJXwt96bkBozHf4vgLR9hXyJDqKHEHKVscAARuD7in5wV1meEcSTJTanCTDzidTe2cFXS1F` - -![Qr code](https://raw.githubusercontent.com/haveno-dex/haveno/master/media/donate_monero.png) +

+ Donate Monero
+ 42sjokkT9FmiWPqVzrWPFE5NCJXwt96bkBozHf4vgLR9hXyJDqKHEHKVscAARuD7in5wV1meEcSTJTanCTDzidTe2cFXS1F +

If you are using a wallet that supports OpenAlias (like the 'official' CLI and GUI wallets), you can simply put `fund@haveno.exchange` as the "receiver" address. ### Bitcoin -`1AKq3CE1yBAnxGmHXbNFfNYStcByNDc5gQ` - -![Qr code](https://raw.githubusercontent.com/haveno-dex/haveno/master/media/donate_bitcoin.png) +

+ Donate Bitcoin
+ 1AKq3CE1yBAnxGmHXbNFfNYStcByNDc5gQ +

From cae360b6c59e28db07e45d91facba3a75da20697 Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 30 Aug 2024 12:12:01 -0400 Subject: [PATCH 15/18] throttle warnings when monerod not synced within tolerance --- .../java/haveno/core/api/XmrConnectionService.java | 14 ++++++++++---- .../main/java/haveno/core/trade/HavenoUtils.java | 1 + .../haveno/core/xmr/wallet/XmrWalletService.java | 9 ++++++++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/haveno/core/api/XmrConnectionService.java b/core/src/main/java/haveno/core/api/XmrConnectionService.java index 48538ef4..4465fd2a 100644 --- a/core/src/main/java/haveno/core/api/XmrConnectionService.java +++ b/core/src/main/java/haveno/core/api/XmrConnectionService.java @@ -99,9 +99,10 @@ public final class XmrConnectionService { @Getter private MoneroDaemonInfo lastInfo; private Long lastLogPollErrorTimestamp; - private Long syncStartHeight = null; + private long lastLogDaemonNotSyncedTimestamp; + private Long syncStartHeight; private TaskLooper daemonPollLooper; - private long lastRefreshPeriodMs = 0; + private long lastRefreshPeriodMs; @Getter private boolean isShutDownStarted; private List listeners = new ArrayList<>(); @@ -371,7 +372,6 @@ public final class XmrConnectionService { Long targetHeight = getTargetHeight(); if (targetHeight == null) return false; if (targetHeight - chainHeight.get() <= 3) return true; // synced if within 3 blocks of target height - log.warn("Our chain height: {} is out of sync with peer nodes chain height: {}", chainHeight.get(), targetHeight); return false; } @@ -720,7 +720,7 @@ public final class XmrConnectionService { } // log error message periodically - if ((lastLogPollErrorTimestamp == null || System.currentTimeMillis() - lastLogPollErrorTimestamp > HavenoUtils.LOG_POLL_ERROR_PERIOD_MS)) { + if (lastLogPollErrorTimestamp == null || System.currentTimeMillis() - lastLogPollErrorTimestamp > HavenoUtils.LOG_POLL_ERROR_PERIOD_MS) { log.warn("Failed to fetch daemon info, trying to switch to best connection: " + e.getMessage()); if (DevEnv.isDevMode()) e.printStackTrace(); lastLogPollErrorTimestamp = System.currentTimeMillis(); @@ -734,6 +734,12 @@ public final class XmrConnectionService { // connected to daemon isConnected = true; + // throttle warnings if daemon not synced + if (!isSyncedWithinTolerance() && System.currentTimeMillis() - lastLogDaemonNotSyncedTimestamp > HavenoUtils.LOG_DAEMON_NOT_SYNCED_WARN_PERIOD_MS) { + log.warn("Our chain height: {} is out of sync with peer nodes chain height: {}", chainHeight.get(), getTargetHeight()); + lastLogDaemonNotSyncedTimestamp = System.currentTimeMillis(); + } + // announce connection change if refresh period changes if (getRefreshPeriodMs() != lastRefreshPeriodMs) { lastRefreshPeriodMs = getRefreshPeriodMs(); diff --git a/core/src/main/java/haveno/core/trade/HavenoUtils.java b/core/src/main/java/haveno/core/trade/HavenoUtils.java index 994753e9..5b9386af 100644 --- a/core/src/main/java/haveno/core/trade/HavenoUtils.java +++ b/core/src/main/java/haveno/core/trade/HavenoUtils.java @@ -81,6 +81,7 @@ public class HavenoUtils { // other configuration public static final long LOG_POLL_ERROR_PERIOD_MS = 1000 * 60 * 4; // log poll errors up to once every 4 minutes + public static final long LOG_DAEMON_NOT_SYNCED_WARN_PERIOD_MS = 1000 * 30; // log warnings when daemon not synced once every 30s // synchronize requests to the daemon private static boolean SYNC_DAEMON_REQUESTS = false; // sync long requests to daemon (e.g. refresh, update pool) // TODO: performance suffers by syncing daemon requests, but otherwise we sometimes get sporadic errors? 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 ea0d77fb..d3402061 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -151,6 +151,7 @@ public class XmrWalletService extends XmrWalletBase { private TaskLooper pollLooper; private boolean pollInProgress; private Long pollPeriodMs; + private long lastLogDaemonNotSyncedTimestamp; private long lastLogPollErrorTimestamp; private long lastPollTxsTimestamp; private final Object pollLock = new Object(); @@ -1767,9 +1768,15 @@ public class XmrWalletService extends XmrWalletBase { return; } if (!xmrConnectionService.isSyncedWithinTolerance()) { - log.warn("Monero daemon is not synced within tolerance, height={}, targetHeight={}", xmrConnectionService.chainHeightProperty().get(), xmrConnectionService.getTargetHeight()); + + // throttle warnings + if (System.currentTimeMillis() - lastLogDaemonNotSyncedTimestamp > HavenoUtils.LOG_DAEMON_NOT_SYNCED_WARN_PERIOD_MS) { + log.warn("Monero daemon is not synced within tolerance, height={}, targetHeight={}, monerod={}", xmrConnectionService.chainHeightProperty().get(), xmrConnectionService.getTargetHeight(), xmrConnectionService.getConnection() == null ? null : xmrConnectionService.getConnection().getUri()); + lastLogDaemonNotSyncedTimestamp = System.currentTimeMillis(); + } return; } + // sync wallet if behind daemon if (walletHeight.get() < xmrConnectionService.getTargetHeight()) { synchronized (walletLock) { // avoid long sync from blocking other operations From 2d0f200aa1131303bb77c460fbb5fa30781dfc0b Mon Sep 17 00:00:00 2001 From: woodser Date: Wed, 21 Aug 2024 10:04:12 -0400 Subject: [PATCH 16/18] do not reprocess error on submit multisig tx w/o enough signers #1227 --- .../core/support/dispute/arbitration/ArbitrationManager.java | 1 + core/src/main/java/haveno/core/trade/HavenoUtils.java | 4 ++++ core/src/main/java/haveno/core/trade/Trade.java | 2 ++ 3 files changed, 7 insertions(+) diff --git a/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java b/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java index 0ac6aa43..8082aad4 100644 --- a/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java +++ b/core/src/main/java/haveno/core/support/dispute/arbitration/ArbitrationManager.java @@ -511,6 +511,7 @@ public final class ArbitrationManager extends DisputeManager Date: Tue, 3 Sep 2024 12:44:58 -0400 Subject: [PATCH 17/18] automatically restore backup cache if corrupt --- .../java/haveno/common/file/FileUtil.java | 20 +++- .../core/xmr/wallet/XmrWalletService.java | 102 ++++++++++++++++-- 2 files changed, 111 insertions(+), 11 deletions(-) diff --git a/common/src/main/java/haveno/common/file/FileUtil.java b/common/src/main/java/haveno/common/file/FileUtil.java index 60ac2c3a..449faea6 100644 --- a/common/src/main/java/haveno/common/file/FileUtil.java +++ b/common/src/main/java/haveno/common/file/FileUtil.java @@ -40,10 +40,13 @@ import java.util.Scanner; @Slf4j public class FileUtil { + + private static final String BACKUP_DIR = "backup"; + public static void rollingBackup(File dir, String fileName, int numMaxBackupFiles) { if (numMaxBackupFiles <= 0) return; if (dir.exists()) { - File backupDir = new File(Paths.get(dir.getAbsolutePath(), "backup").toString()); + File backupDir = new File(Paths.get(dir.getAbsolutePath(), BACKUP_DIR).toString()); if (!backupDir.exists()) if (!backupDir.mkdir()) log.warn("make dir failed.\nBackupDir=" + backupDir.getAbsolutePath()); @@ -72,8 +75,21 @@ public class FileUtil { } } + public static File getLatestBackupFile(File dir, String fileName) { + File backupDir = new File(Paths.get(dir.getAbsolutePath(), BACKUP_DIR).toString()); + if (!backupDir.exists()) return null; + String dirName = "backups_" + fileName; + if (dirName.contains(".")) dirName = dirName.replace(".", "_"); + 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]; + } + public static void deleteRollingBackup(File dir, String fileName) { - File backupDir = new File(Paths.get(dir.getAbsolutePath(), "backup").toString()); + File backupDir = new File(Paths.get(dir.getAbsolutePath(), BACKUP_DIR).toString()); if (!backupDir.exists()) return; String dirName = "backups_" + fileName; if (dirName.contains(".")) dirName = dirName.replace(".", "_"); 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 d3402061..325bdc13 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -136,7 +136,6 @@ public class XmrWalletService extends XmrWalletBase { private final WalletsSetup walletsSetup; private final File walletDir; - private final File xmrWalletFile; private final int rpcBindPort; private final boolean useNativeXmrWallet; protected final CopyOnWriteArraySet balanceListeners = new CopyOnWriteArraySet<>(); @@ -181,7 +180,6 @@ public class XmrWalletService extends XmrWalletBase { this.walletDir = walletDir; this.rpcBindPort = rpcBindPort; this.useNativeXmrWallet = useNativeXmrWallet; - this.xmrWalletFile = new File(walletDir, MONERO_WALLET_NAME); HavenoUtils.xmrWalletService = this; HavenoUtils.xmrConnectionService = xmrConnectionService; this.xmrConnectionService = xmrConnectionService; // TODO: super's is null unless set here from injection @@ -1327,7 +1325,7 @@ public class XmrWalletService extends XmrWalletBase { if (wallet == null) { MoneroDaemonRpc daemon = xmrConnectionService.getDaemon(); log.info("Initializing main wallet with monerod=" + (daemon == null ? "null" : daemon.getRpcConnection().getUri())); - if (MoneroUtils.walletExists(xmrWalletFile.getPath())) { + if (walletExists(MONERO_WALLET_NAME)) { wallet = openWallet(MONERO_WALLET_NAME, rpcBindPort, isProxyApplied(wasWalletSynced)); } else if (Boolean.TRUE.equals(xmrConnectionService.isConnected())) { wallet = createWallet(MONERO_WALLET_NAME, rpcBindPort); @@ -1475,11 +1473,54 @@ public class XmrWalletService extends XmrWalletBase { MoneroRpcConnection connection = new MoneroRpcConnection(xmrConnectionService.getConnection()); if (!applyProxyUri) connection.setProxyUri(null); - // open wallet + // try opening wallet config.setNetworkType(getMoneroNetworkType()); config.setServer(connection); log.info("Opening full wallet " + config.getPath() + " with monerod=" + connection.getUri() + ", proxyUri=" + connection.getProxyUri()); - walletFull = MoneroWalletFull.openWallet(config); + 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()); + boolean retrySuccessful = false; + try { + + // rename wallet cache to backup + String cachePath = walletDir.toString() + File.separator + MONERO_WALLET_NAME; + 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); + 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"); + retrySuccessful = true; + } catch (Exception e2) { + // ignore + } + + // handle success or failure + if (retrySuccessful) { + originalCacheFile.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(); + File originalCacheBackup = new File(cachePath + ".backup"); + if (originalCacheBackup.exists()) originalCacheBackup.renameTo(new File(cachePath)); + + // throw exception + throw e; + } + } catch (Exception e2) { + throw e; // throw original exception + } + } if (walletFull.getDaemonConnection() != null) walletFull.getDaemonConnection().setPrintStackTrace(PRINT_RPC_STACK_TRACE); log.info("Done opening full wallet " + config.getPath()); return walletFull; @@ -1518,7 +1559,7 @@ public class XmrWalletService extends XmrWalletBase { } catch (Exception e) { e.printStackTrace(); if (walletRpc != null) forceCloseWallet(walletRpc, config.getPath()); - throw new IllegalStateException("Could not create wallet '" + config.getPath() + "'. Please close Haveno, stop all monero-wallet-rpc processes, and restart Haveno."); + throw new IllegalStateException("Could not create wallet '" + config.getPath() + "'. Please close Haveno, stop all monero-wallet-rpc processes in your task manager, and restart Haveno."); } } @@ -1537,17 +1578,60 @@ public class XmrWalletService extends XmrWalletBase { MoneroRpcConnection connection = new MoneroRpcConnection(xmrConnectionService.getConnection()); if (!applyProxyUri) connection.setProxyUri(null); - // open wallet + // try opening wallet log.info("Opening RPC wallet " + config.getPath() + " with monerod=" + connection.getUri() + ", proxyUri=" + connection.getProxyUri()); config.setServer(connection); - walletRpc.openWallet(config); + try { + walletRpc.openWallet(config); + } catch (Exception e) { + log.warn("Failed to open RPC wallet '{}', attempting to use backup cache, error={}", config.getPath(), e.getMessage()); + boolean retrySuccessful = false; + try { + + // rename wallet cache to backup + String cachePath = walletDir.toString() + File.separator + MONERO_WALLET_NAME; + 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); + 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"); + retrySuccessful = true; + } catch (Exception e2) { + // ignore + } + + // handle success or failure + if (retrySuccessful) { + originalCacheFile.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(); + File originalCacheBackup = new File(cachePath + ".backup"); + if (originalCacheBackup.exists()) originalCacheBackup.renameTo(new File(cachePath)); + + // throw exception + throw e; + } + } catch (Exception e2) { + throw e; // throw original exception + } + } if (walletRpc.getDaemonConnection() != null) walletRpc.getDaemonConnection().setPrintStackTrace(PRINT_RPC_STACK_TRACE); log.info("Done opening RPC wallet " + config.getPath()); return walletRpc; } catch (Exception e) { e.printStackTrace(); if (walletRpc != null) forceCloseWallet(walletRpc, config.getPath()); - throw new IllegalStateException("Could not open wallet '" + config.getPath() + "'. Please close Haveno, stop all monero-wallet-rpc processes, and restart Haveno.\n\nError message: " + e.getMessage()); + throw new IllegalStateException("Could not open wallet '" + config.getPath() + "'. Please close Haveno, stop all monero-wallet-rpc processes in your task manager, and restart Haveno.\n\nError message: " + e.getMessage()); } } From a20377fb048d64cf65e199c54252954311a9790d Mon Sep 17 00:00:00 2001 From: woodser Date: Wed, 4 Sep 2024 08:37:49 -0400 Subject: [PATCH 18/18] bump version to 1.0.11 --- 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 4a24e5fc..83d42efb 100644 --- a/build.gradle +++ b/build.gradle @@ -609,7 +609,7 @@ configure(project(':desktop')) { apply plugin: 'com.github.johnrengelman.shadow' apply from: 'package/package.gradle' - version = '1.0.10-SNAPSHOT' + version = '1.0.11-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 c8ab2551..e224c13d 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.10"; + public static final String VERSION = "1.0.11"; /** * 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 29c4932b..5a058b31 100644 --- a/desktop/package/macosx/Info.plist +++ b/desktop/package/macosx/Info.plist @@ -5,10 +5,10 @@ CFBundleVersion - 1.0.10 + 1.0.11 CFBundleShortVersionString - 1.0.10 + 1.0.11 CFBundleExecutable Haveno diff --git a/seednode/src/main/java/haveno/seednode/SeedNodeMain.java b/seednode/src/main/java/haveno/seednode/SeedNodeMain.java index dedb7d1c..5659ab2e 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.10"; + private static final String VERSION = "1.0.11"; private SeedNode seedNode; private Timer checkConnectionLossTime;