diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index df836c316f..5920f8c17a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ jobs: build: strategy: matrix: - os: [ubuntu-latest, macos-13, windows-latest] + os: [ubuntu-22.04, macos-13, windows-latest] fail-fast: false runs-on: ${{ matrix.os }} steps: @@ -37,7 +37,7 @@ jobs: name: cached-localnet path: .localnet - name: Install dependencies - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os == 'ubuntu-22.04' }} run: | sudo apt update sudo apt install -y rpm flatpak flatpak-builder appstream @@ -56,7 +56,7 @@ jobs: # get version from jar - name: Set Version Unix - if: ${{ matrix.os == 'ubuntu-latest' || matrix.os == 'macos-13' }} + if: ${{ matrix.os == 'ubuntu-22.04' || matrix.os == 'macos-13' }} run: | export VERSION=$(ls desktop/build/temp-*/binaries/desktop-*.jar.SHA-256 | grep -Eo 'desktop-[0-9]+\.[0-9]+\.[0-9]+' | sed 's/desktop-//') echo "VERSION=$VERSION" >> $GITHUB_ENV @@ -66,55 +66,64 @@ jobs: $VERSION = (Get-ChildItem -Path desktop\build\temp-*/binaries\desktop-*.jar.SHA-256).Name -replace 'desktop-', '' -replace '-.*', '' "VERSION=$VERSION" | Out-File -FilePath $env:GITHUB_ENV -Append shell: powershell - + - name: Move Release Files on Unix - if: ${{ matrix.os == 'ubuntu-latest' || matrix.os == 'macos-13' }} + if: ${{ matrix.os == 'ubuntu-22.04' || matrix.os == 'macos-13' }} run: | mkdir ${{ github.workspace }}/release - if [ "${{ matrix.os }}" == "ubuntu-latest" ]; then - mv desktop/build/temp-*/binaries/haveno-*.rpm ${{ github.workspace }}/release - mv desktop/build/temp-*/binaries/haveno_*.deb ${{ github.workspace }}/release - mv desktop/build/temp-*/binaries/*.flatpak ${{ github.workspace }}/release + if [ "${{ matrix.os }}" == "ubuntu-22.04" ]; then + mkdir ${{ github.workspace }}/release-rpm + mkdir ${{ github.workspace }}/release-deb + mkdir ${{ github.workspace }}/release-flat + mv desktop/build/temp-*/binaries/haveno-*.rpm ${{ github.workspace }}/release-rpm/Haveno-${{ env.VERSION }}-x86_64.rpm + mv desktop/build/temp-*/binaries/haveno_*.deb ${{ github.workspace }}/release-deb/Haveno-${{ env.VERSION }}-x86_64.deb + mv desktop/build/temp-*/binaries/*.flatpak ${{ github.workspace }}/release-flat + else - mv desktop/build/temp-*/binaries/Haveno-*.dmg ${{ github.workspace }}/release + mv desktop/build/temp-*/binaries/Haveno-*.dmg ${{ github.workspace }}/release/Haveno-${{ env.VERSION }}.dmg fi - mv desktop/build/temp-*/binaries/desktop-*.jar.SHA-256 ${{ github.workspace }}/release + cp desktop/build/temp-*/binaries/desktop-*.jar.SHA-256 ${{ github.workspace }}/release-flat + cp desktop/build/temp-*/binaries/desktop-*.jar.SHA-256 ${{ github.workspace }}/release-deb + cp desktop/build/temp-*/binaries/desktop-*.jar.SHA-256 ${{ github.workspace }}/release-rpm shell: bash - name: Move Release Files on Windows if: ${{ matrix.os == 'windows-latest' }} run: | mkdir ${{ github.workspace }}/release - Move-Item -Path desktop\build\temp-*/binaries\Haveno-*.exe -Destination ${{ github.workspace }}/release + Move-Item -Path desktop\build\temp-*/binaries\Haveno-*.exe -Destination ${{ github.workspace }}/release/Haveno-${{ env.VERSION }}.exe Move-Item -Path desktop\build\temp-*/binaries\desktop-*.jar.SHA-256 -Destination ${{ github.workspace }}/release shell: powershell - - # do all files manually and use version too # win - uses: actions/upload-artifact@v3 - name: "Windows - exe artifact" + name: "Windows artifacts" + if: ${{ matrix.os == 'windows-latest'}} with: - name: windows-haveno-${{ env.VERSION }}.exe - path: ${{ github.workspace }}/release/Haveno-*.exe + name: haveno-windows + path: ${{ github.workspace }}/release # macos - uses: actions/upload-artifact@v3 - name: "MacOS - dmg artifact" + name: "macOS artifacts" + if: ${{ matrix.os == 'macos-13' }} with: - name: macos-haveno-${{ env.VERSION }}.dmg - path: ${{ github.workspace }}/release/Haveno-*.dmg + name: haveno-macos + path: ${{ github.workspace }}/release # linux - uses: actions/upload-artifact@v3 name: "Linux - deb artifact" + if: ${{ matrix.os == 'ubuntu-22.04' }} with: - name: linux-haveno-${{ env.VERSION }}.deb - path: ${{ github.workspace }}/release/haveno_*.deb + name: haveno-linux-deb + path: ${{ github.workspace }}/release-deb - uses: actions/upload-artifact@v3 name: "Linux - rpm artifact" + if: ${{ matrix.os == 'ubuntu-22.04' }} with: - name: linux-haveno-${{ env.VERSION }}.rpm - path: ${{ github.workspace }}/release/haveno-*.rpm + name: haveno-linux-rpm + path: ${{ github.workspace }}/release-rpm - uses: actions/upload-artifact@v3 name: "Linux - flatpak artifact" + if: ${{ matrix.os == 'ubuntu-22.04' }} with: - name: linux-haveno-${{ env.VERSION }}.flatpak - path: ${{ github.workspace }}/release/*.flatpak + name: haveno-linux-flatpak + path: ${{ github.workspace }}/release-flat diff --git a/README.md b/README.md index 465e04f912..7f4d1eb2eb 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ To bring Haveno to life, we need resources. If you have the possibility, please ### Monero

- Donate Monero
+ Donate Monero
42sjokkT9FmiWPqVzrWPFE5NCJXwt96bkBozHf4vgLR9hXyJDqKHEHKVscAARuD7in5wV1meEcSTJTanCTDzidTe2cFXS1F

@@ -81,6 +81,6 @@ If you are using a wallet that supports OpenAlias (like the 'official' CLI and G ### Bitcoin

- Donate Bitcoin
+ Donate Bitcoin
1AKq3CE1yBAnxGmHXbNFfNYStcByNDc5gQ

diff --git a/apitest/src/main/java/haveno/apitest/ApiTestMain.java b/apitest/src/main/java/haveno/apitest/ApiTestMain.java index 4da4b92061..ad383ff1ef 100644 --- a/apitest/src/main/java/haveno/apitest/ApiTestMain.java +++ b/apitest/src/main/java/haveno/apitest/ApiTestMain.java @@ -78,7 +78,7 @@ public class ApiTestMain { } catch (Throwable ex) { err.println("Fault: An unexpected error occurred. " + - "Please file a report at https://haveno.exchange/issues"); + "Please file a report at https://github.com/haveno-dex/haveno/issues"); ex.printStackTrace(err); exit(EXIT_FAILURE); } diff --git a/build.gradle b/build.gradle index 83d42efbbe..bcd7d082b0 100644 --- a/build.gradle +++ b/build.gradle @@ -49,7 +49,7 @@ configure(subprojects) { gsonVersion = '2.8.5' guavaVersion = '32.1.1-jre' guiceVersion = '7.0.0' - moneroJavaVersion = '0.8.31' + moneroJavaVersion = '0.8.33' httpclient5Version = '5.0' hamcrestVersion = '2.2' httpclientVersion = '4.5.12' @@ -334,6 +334,7 @@ configure(project(':p2p')) { implementation "com.google.protobuf:protobuf-java:$protobufVersion" implementation "org.fxmisc.easybind:easybind:$easybindVersion" implementation "org.slf4j:slf4j-api:$slf4jVersion" + implementation "org.apache.commons:commons-lang3:$langVersion" implementation("com.github.haveno-dex.netlayer:tor.external:$netlayerVersion") { exclude(module: 'slf4j-api') } diff --git a/common/src/main/java/haveno/common/app/Log.java b/common/src/main/java/haveno/common/app/Log.java index 1922a519eb..67ca2ab9ed 100644 --- a/common/src/main/java/haveno/common/app/Log.java +++ b/common/src/main/java/haveno/common/app/Log.java @@ -21,6 +21,7 @@ import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.encoder.PatternLayoutEncoder; +import ch.qos.logback.classic.filter.ThresholdFilter; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.rolling.FixedWindowRollingPolicy; import ch.qos.logback.core.rolling.RollingFileAppender; @@ -52,11 +53,12 @@ public class Log { SizeBasedTriggeringPolicy triggeringPolicy = new SizeBasedTriggeringPolicy<>(); triggeringPolicy.setMaxFileSize(FileSize.valueOf("10MB")); + triggeringPolicy.setContext(loggerContext); triggeringPolicy.start(); PatternLayoutEncoder encoder = new PatternLayoutEncoder(); encoder.setContext(loggerContext); - encoder.setPattern("%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{15}: %msg %xEx%n"); + encoder.setPattern("%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{15}: %msg%n"); encoder.start(); appender.setEncoder(encoder); @@ -64,25 +66,43 @@ public class Log { appender.setTriggeringPolicy(triggeringPolicy); appender.start(); - logbackLogger = loggerContext.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); - logbackLogger.addAppender(appender); - logbackLogger.setLevel(Level.INFO); - // log errors in separate file - // not working as expected still.... damn logback... - /* FileAppender errorAppender = new FileAppender(); - errorAppender.setEncoder(encoder); + PatternLayoutEncoder errorEncoder = new PatternLayoutEncoder(); + errorEncoder.setContext(loggerContext); + errorEncoder.setPattern("%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger: %msg%n%ex"); + errorEncoder.start(); + + RollingFileAppender errorAppender = new RollingFileAppender<>(); + errorAppender.setEncoder(errorEncoder); errorAppender.setName("Error"); errorAppender.setContext(loggerContext); errorAppender.setFile(fileName + "_error.log"); - LevelFilter levelFilter = new LevelFilter(); - levelFilter.setLevel(Level.ERROR); - levelFilter.setOnMatch(FilterReply.ACCEPT); - levelFilter.setOnMismatch(FilterReply.DENY); - levelFilter.start(); - errorAppender.addFilter(levelFilter); + + FixedWindowRollingPolicy errorRollingPolicy = new FixedWindowRollingPolicy(); + errorRollingPolicy.setContext(loggerContext); + errorRollingPolicy.setParent(errorAppender); + errorRollingPolicy.setFileNamePattern(fileName + "_error_%i.log"); + errorRollingPolicy.setMinIndex(1); + errorRollingPolicy.setMaxIndex(20); + errorRollingPolicy.start(); + + SizeBasedTriggeringPolicy errorTriggeringPolicy = new SizeBasedTriggeringPolicy<>(); + errorTriggeringPolicy.setMaxFileSize(FileSize.valueOf("10MB")); + errorTriggeringPolicy.start(); + + ThresholdFilter thresholdFilter = new ThresholdFilter(); + thresholdFilter.setLevel("WARN"); + thresholdFilter.start(); + + errorAppender.setRollingPolicy(errorRollingPolicy); + errorAppender.setTriggeringPolicy(errorTriggeringPolicy); + errorAppender.addFilter(thresholdFilter); errorAppender.start(); - logbackLogger.addAppender(errorAppender);*/ + + logbackLogger = loggerContext.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); + logbackLogger.addAppender(errorAppender); + logbackLogger.addAppender(appender); + logbackLogger.setLevel(Level.INFO); } public static void setCustomLogLevel(String pattern, Level logLevel) { diff --git a/common/src/main/java/haveno/common/file/FileUtil.java b/common/src/main/java/haveno/common/file/FileUtil.java index 449faea64b..27058f3025 100644 --- a/common/src/main/java/haveno/common/file/FileUtil.java +++ b/common/src/main/java/haveno/common/file/FileUtil.java @@ -68,8 +68,7 @@ public class FileUtil { pruneBackup(backupFileDir, numMaxBackupFiles); } catch (IOException e) { - log.error("Backup key failed: " + e.getMessage()); - e.printStackTrace(); + log.error("Backup key failed: {}\n", e.getMessage(), e); } } } @@ -97,7 +96,7 @@ public class FileUtil { try { FileUtils.deleteDirectory(backupFileDir); } catch (IOException e) { - e.printStackTrace(); + log.error("Delete backup key failed: {}\n", e.getMessage(), e); } } @@ -173,8 +172,7 @@ public class FileUtil { } } } catch (Throwable t) { - log.error(t.toString()); - t.printStackTrace(); + log.error("Could not delete file, error={}\n", t.getMessage(), t); throw new IOException(t); } } diff --git a/common/src/main/java/haveno/common/setup/CommonSetup.java b/common/src/main/java/haveno/common/setup/CommonSetup.java index f606d3b534..0c929b3ae4 100644 --- a/common/src/main/java/haveno/common/setup/CommonSetup.java +++ b/common/src/main/java/haveno/common/setup/CommonSetup.java @@ -69,11 +69,7 @@ public class CommonSetup { "The system tray is not supported on the current platform.".equals(throwable.getMessage())) { log.warn(throwable.getMessage()); } else { - log.error("Uncaught Exception from thread " + Thread.currentThread().getName()); - log.error("throwableMessage= " + throwable.getMessage()); - log.error("throwableClass= " + throwable.getClass()); - log.error("Stack trace:\n" + ExceptionUtils.getStackTrace(throwable)); - throwable.printStackTrace(); + log.error("Uncaught Exception from thread {}, error={}\n", Thread.currentThread().getName(), throwable.getMessage(), throwable); UserThread.execute(() -> uncaughtExceptionHandler.handleUncaughtException(throwable, false)); } }; @@ -113,8 +109,7 @@ public class CommonSetup { if (!pathOfCodeSource.endsWith("classes")) log.info("Path to Haveno jar file: " + pathOfCodeSource); } catch (URISyntaxException e) { - log.error(e.toString()); - e.printStackTrace(); + log.error(ExceptionUtils.getStackTrace(e)); } } } diff --git a/common/src/main/java/haveno/common/taskrunner/TaskRunner.java b/common/src/main/java/haveno/common/taskrunner/TaskRunner.java index e49b4ccd91..087ffce702 100644 --- a/common/src/main/java/haveno/common/taskrunner/TaskRunner.java +++ b/common/src/main/java/haveno/common/taskrunner/TaskRunner.java @@ -25,6 +25,8 @@ import java.util.Arrays; import java.util.Queue; import java.util.concurrent.LinkedBlockingQueue; +import org.apache.commons.lang3.exception.ExceptionUtils; + @Slf4j public class TaskRunner { private final Queue>> tasks = new LinkedBlockingQueue<>(); @@ -67,8 +69,8 @@ public class TaskRunner { log.info("Run task: " + currentTask.getSimpleName()); currentTask.getDeclaredConstructor(TaskRunner.class, sharedModelClass).newInstance(this, sharedModel).run(); } catch (Throwable throwable) { - throwable.printStackTrace(); - handleErrorMessage("Error at taskRunner: " + throwable.getMessage()); + log.error(ExceptionUtils.getStackTrace(throwable)); + handleErrorMessage("Error at taskRunner, error=" + throwable.getMessage()); } } else { resultHandler.handleResult(); diff --git a/common/src/main/java/haveno/common/util/Utilities.java b/common/src/main/java/haveno/common/util/Utilities.java index b4afe417e8..240ea49ee9 100644 --- a/common/src/main/java/haveno/common/util/Utilities.java +++ b/common/src/main/java/haveno/common/util/Utilities.java @@ -331,8 +331,7 @@ public class Utilities { clipboard.setContent(clipboardContent); } } catch (Throwable e) { - log.error("copyToClipboard failed " + e.getMessage()); - e.printStackTrace(); + log.error("copyToClipboard failed: {}\n", e.getMessage(), e); } } diff --git a/core/src/main/java/haveno/core/api/CoreApi.java b/core/src/main/java/haveno/core/api/CoreApi.java index 25f98c3470..9afc4ee2b9 100644 --- a/core/src/main/java/haveno/core/api/CoreApi.java +++ b/core/src/main/java/haveno/core/api/CoreApi.java @@ -260,11 +260,11 @@ public class CoreApi { } public void startXmrNode(XmrNodeSettings settings) throws IOException { - xmrLocalNode.startNode(settings); + xmrLocalNode.start(settings); } public void stopXmrNode() { - xmrLocalNode.stopNode(); + xmrLocalNode.stop(); } /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/haveno/core/api/CoreDisputesService.java b/core/src/main/java/haveno/core/api/CoreDisputesService.java index 7edbed9b01..f193287edc 100644 --- a/core/src/main/java/haveno/core/api/CoreDisputesService.java +++ b/core/src/main/java/haveno/core/api/CoreDisputesService.java @@ -52,6 +52,9 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Optional; + +import org.apache.commons.lang3.exception.ExceptionUtils; + import lombok.extern.slf4j.Slf4j; @@ -204,7 +207,7 @@ public class CoreDisputesService { throw new IllegalStateException(errMessage, err); }); } catch (Exception e) { - e.printStackTrace(); + log.error(ExceptionUtils.getStackTrace(e)); throw new IllegalStateException(e.getMessage() == null ? ("Error resolving dispute for trade " + trade.getId()) : e.getMessage()); } } diff --git a/core/src/main/java/haveno/core/api/CoreNotificationService.java b/core/src/main/java/haveno/core/api/CoreNotificationService.java index 6c40d7498b..484208d06d 100644 --- a/core/src/main/java/haveno/core/api/CoreNotificationService.java +++ b/core/src/main/java/haveno/core/api/CoreNotificationService.java @@ -3,7 +3,11 @@ package haveno.core.api; import com.google.inject.Singleton; import haveno.core.api.model.TradeInfo; import haveno.core.support.messages.ChatMessage; +import haveno.core.trade.HavenoUtils; +import haveno.core.trade.MakerTrade; +import haveno.core.trade.SellerTrade; import haveno.core.trade.Trade; +import haveno.core.trade.Trade.Phase; import haveno.proto.grpc.NotificationMessage; import haveno.proto.grpc.NotificationMessage.NotificationType; import java.util.Iterator; @@ -46,7 +50,15 @@ public class CoreNotificationService { .build()); } - public void sendTradeNotification(Trade trade, String title, String message) { + public void sendTradeNotification(Trade trade, Phase phase, String title, String message) { + + // play chime when maker's trade is taken + if (trade instanceof MakerTrade && phase == Trade.Phase.DEPOSITS_PUBLISHED) HavenoUtils.playChimeSound(); + + // play chime when seller sees buyer confirm payment sent + if (trade instanceof SellerTrade && phase == Trade.Phase.PAYMENT_SENT) HavenoUtils.playChimeSound(); + + // send notification sendNotification(NotificationMessage.newBuilder() .setType(NotificationType.TRADE_UPDATE) .setTrade(TradeInfo.toTradeInfo(trade).toProtoMessage()) @@ -57,6 +69,7 @@ public class CoreNotificationService { } public void sendChatNotification(ChatMessage chatMessage) { + HavenoUtils.playChimeSound(); sendNotification(NotificationMessage.newBuilder() .setType(NotificationType.CHAT_MESSAGE) .setTimestamp(System.currentTimeMillis()) diff --git a/core/src/main/java/haveno/core/api/CoreTradesService.java b/core/src/main/java/haveno/core/api/CoreTradesService.java index 16fa22a8db..431ab9a652 100644 --- a/core/src/main/java/haveno/core/api/CoreTradesService.java +++ b/core/src/main/java/haveno/core/api/CoreTradesService.java @@ -66,6 +66,8 @@ import java.util.List; import java.util.Optional; import java.util.function.Consumer; import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.lang3.exception.ExceptionUtils; import org.bitcoinj.core.Coin; @Singleton @@ -161,7 +163,7 @@ class CoreTradesService { errorMessageHandler ); } catch (Exception e) { - e.printStackTrace(); + log.error(ExceptionUtils.getStackTrace(e)); errorMessageHandler.handleErrorMessage(e.getMessage()); } } diff --git a/core/src/main/java/haveno/core/api/XmrConnectionService.java b/core/src/main/java/haveno/core/api/XmrConnectionService.java index 4465fd2a35..ffeacbfd38 100644 --- a/core/src/main/java/haveno/core/api/XmrConnectionService.java +++ b/core/src/main/java/haveno/core/api/XmrConnectionService.java @@ -41,6 +41,9 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; + +import org.apache.commons.lang3.exception.ExceptionUtils; + import javafx.beans.property.IntegerProperty; import javafx.beans.property.LongProperty; import javafx.beans.property.ObjectProperty; @@ -464,7 +467,7 @@ public final class XmrConnectionService { log.info(getClass() + ".onAccountOpened() called"); initialize(); } catch (Exception e) { - e.printStackTrace(); + log.error("Error initializing connection service after account opened, error={}\n", e.getMessage(), e); throw new RuntimeException(e); } } @@ -620,10 +623,9 @@ public final class XmrConnectionService { if (connectionManager.getConnection() != null && xmrLocalNode.equalsUri(connectionManager.getConnection().getUri()) && !xmrLocalNode.isDetected() && !xmrLocalNode.shouldBeIgnored()) { try { log.info("Starting local node"); - xmrLocalNode.startMoneroNode(); + xmrLocalNode.start(); } catch (Exception e) { - log.warn("Unable to start local monero node: " + e.getMessage()); - e.printStackTrace(); + log.error("Unable to start local monero node, error={}\n", e.getMessage(), e); } } } @@ -721,8 +723,8 @@ public final class XmrConnectionService { // log error message periodically 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(); + log.warn("Failed to fetch daemon info, trying to switch to best connection, error={}", e.getMessage()); + if (DevEnv.isDevMode()) log.error(ExceptionUtils.getStackTrace(e)); lastLogPollErrorTimestamp = System.currentTimeMillis(); } @@ -734,6 +736,12 @@ public final class XmrConnectionService { // connected to daemon isConnected = true; + // determine if blockchain is syncing locally + boolean blockchainSyncing = lastInfo.getHeight().equals(lastInfo.getHeightWithoutBootstrap()) || (lastInfo.getTargetHeight().equals(0l) && lastInfo.getHeightWithoutBootstrap().equals(0l)); // blockchain is syncing if height equals height without bootstrap, or target height and height without bootstrap both equal 0 + + // write sync status to preferences + preferences.getXmrNodeSettings().setSyncBlockchain(blockchainSyncing); + // 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()); diff --git a/core/src/main/java/haveno/core/api/XmrLocalNode.java b/core/src/main/java/haveno/core/api/XmrLocalNode.java index 80682e9a34..cd5ed266f1 100644 --- a/core/src/main/java/haveno/core/api/XmrLocalNode.java +++ b/core/src/main/java/haveno/core/api/XmrLocalNode.java @@ -150,16 +150,16 @@ public class XmrLocalNode { /** * Start a local Monero node from settings. */ - public void startMoneroNode() throws IOException { + public void start() throws IOException { var settings = preferences.getXmrNodeSettings(); - this.startNode(settings); + this.start(settings); } /** * Start local Monero node. Throws MoneroError if the node cannot be started. * Persist the settings to preferences if the node started successfully. */ - public void startNode(XmrNodeSettings settings) throws IOException { + public void start(XmrNodeSettings settings) throws IOException { if (isDetected()) throw new IllegalStateException("Local Monero node already online"); log.info("Starting local Monero node: " + settings); @@ -177,6 +177,11 @@ public class XmrLocalNode { args.add("--bootstrap-daemon-address=" + bootstrapUrl); } + var syncBlockchain = settings.getSyncBlockchain(); + if (syncBlockchain != null && !syncBlockchain) { + args.add("--no-sync"); + } + var flags = settings.getStartupFlags(); if (flags != null) { args.addAll(flags); @@ -191,7 +196,7 @@ public class XmrLocalNode { * Stop the current local Monero node if we own its process. * Does not remove the last XmrNodeSettings. */ - public void stopNode() { + public void stop() { if (!isDetected()) throw new IllegalStateException("Local Monero node is not running"); if (daemon.getProcess() == null || !daemon.getProcess().isAlive()) throw new IllegalStateException("Cannot stop local Monero node because we don't own its process"); // TODO (woodser): remove isAlive() check after monero-java 0.5.4 which nullifies internal process daemon.stopProcess(); diff --git a/core/src/main/java/haveno/core/api/model/XmrBalanceInfo.java b/core/src/main/java/haveno/core/api/model/XmrBalanceInfo.java index 2a9e23cd42..76e661aea6 100644 --- a/core/src/main/java/haveno/core/api/model/XmrBalanceInfo.java +++ b/core/src/main/java/haveno/core/api/model/XmrBalanceInfo.java @@ -98,7 +98,7 @@ public class XmrBalanceInfo implements Payload { public String toString() { return "XmrBalanceInfo{" + "balance=" + balance + - "unlockedBalance=" + availableBalance + + ", unlockedBalance=" + availableBalance + ", lockedBalance=" + pendingBalance + ", reservedOfferBalance=" + reservedOfferBalance + ", reservedTradeBalance=" + reservedTradeBalance + diff --git a/core/src/main/java/haveno/core/app/HavenoExecutable.java b/core/src/main/java/haveno/core/app/HavenoExecutable.java index 7070e30ee0..c00eef16ce 100644 --- a/core/src/main/java/haveno/core/app/HavenoExecutable.java +++ b/core/src/main/java/haveno/core/app/HavenoExecutable.java @@ -127,7 +127,7 @@ public abstract class HavenoExecutable System.exit(EXIT_FAILURE); } catch (Throwable ex) { System.err.println("fault: An unexpected error occurred. " + - "Please file a report at https://haveno.exchange/issues"); + "Please file a report at https://github.com/haveno-dex/haveno/issues"); ex.printStackTrace(System.err); System.exit(EXIT_FAILURE); } @@ -204,8 +204,7 @@ public abstract class HavenoExecutable startApplication(); } } catch (InterruptedException | ExecutionException e) { - log.error("An error occurred: {}", e.getMessage()); - e.printStackTrace(); + log.error("An error occurred: {}\n", e.getMessage(), e); } }); } @@ -365,7 +364,7 @@ public abstract class HavenoExecutable try { ThreadUtils.awaitTasks(tasks, tasks.size(), 90000l); // run in parallel with timeout } catch (Exception e) { - e.printStackTrace(); + log.error("Failed to notify all services to prepare for shutdown: {}\n", e.getMessage(), e); } injector.getInstance(TradeManager.class).shutDown(); @@ -400,8 +399,7 @@ public abstract class HavenoExecutable }); }); } catch (Throwable t) { - log.error("App shutdown failed with exception {}", t.toString()); - t.printStackTrace(); + log.error("App shutdown failed with exception: {}\n", t.getMessage(), t); completeShutdown(resultHandler, EXIT_FAILURE, systemExit); } } diff --git a/core/src/main/java/haveno/core/app/HavenoSetup.java b/core/src/main/java/haveno/core/app/HavenoSetup.java index 0d1ee2b996..d80ca807cc 100644 --- a/core/src/main/java/haveno/core/app/HavenoSetup.java +++ b/core/src/main/java/haveno/core/app/HavenoSetup.java @@ -53,6 +53,7 @@ import haveno.core.alert.Alert; import haveno.core.alert.AlertManager; import haveno.core.alert.PrivateNotificationManager; import haveno.core.alert.PrivateNotificationPayload; +import haveno.core.api.CoreContext; import haveno.core.api.XmrConnectionService; import haveno.core.api.XmrLocalNode; import haveno.core.locale.Res; @@ -131,7 +132,10 @@ public class HavenoSetup { private final Preferences preferences; private final User user; private final AlertManager alertManager; + @Getter private final Config config; + @Getter + private final CoreContext coreContext; private final AccountAgeWitnessService accountAgeWitnessService; private final TorSetup torSetup; private final CoinFormatter formatter; @@ -228,6 +232,7 @@ public class HavenoSetup { User user, AlertManager alertManager, Config config, + CoreContext coreContext, AccountAgeWitnessService accountAgeWitnessService, TorSetup torSetup, @Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter, @@ -253,6 +258,7 @@ public class HavenoSetup { this.user = user; this.alertManager = alertManager; this.config = config; + this.coreContext = coreContext; this.accountAgeWitnessService = accountAgeWitnessService; this.torSetup = torSetup; this.formatter = formatter; @@ -263,6 +269,7 @@ public class HavenoSetup { this.arbitrationManager = arbitrationManager; HavenoUtils.havenoSetup = this; + HavenoUtils.preferences = preferences; } /////////////////////////////////////////////////////////////////////////////////////////// @@ -376,8 +383,7 @@ public class HavenoSetup { moneroWalletRpcFile.setExecutable(true); } } catch (Exception e) { - e.printStackTrace(); - log.warn("Failed to install Monero binaries: " + e.toString()); + log.warn("Failed to install Monero binaries: {}\n", e.getMessage(), e); } } diff --git a/core/src/main/java/haveno/core/app/TorSetup.java b/core/src/main/java/haveno/core/app/TorSetup.java index d878464af2..c28e509ecc 100644 --- a/core/src/main/java/haveno/core/app/TorSetup.java +++ b/core/src/main/java/haveno/core/app/TorSetup.java @@ -28,6 +28,9 @@ import java.io.File; import java.io.IOException; import java.nio.file.Paths; import javax.annotation.Nullable; + +import org.apache.commons.lang3.exception.ExceptionUtils; + import lombok.extern.slf4j.Slf4j; @Slf4j @@ -48,8 +51,7 @@ public class TorSetup { if (resultHandler != null) resultHandler.run(); } catch (IOException e) { - e.printStackTrace(); - log.error(e.toString()); + log.error(ExceptionUtils.getStackTrace(e)); if (errorMessageHandler != null) errorMessageHandler.handleErrorMessage(e.toString()); } diff --git a/core/src/main/java/haveno/core/app/misc/ExecutableForAppWithP2p.java b/core/src/main/java/haveno/core/app/misc/ExecutableForAppWithP2p.java index 8086d563d1..725ccd877c 100644 --- a/core/src/main/java/haveno/core/app/misc/ExecutableForAppWithP2p.java +++ b/core/src/main/java/haveno/core/app/misc/ExecutableForAppWithP2p.java @@ -123,7 +123,7 @@ public abstract class ExecutableForAppWithP2p extends HavenoExecutable { try { ThreadUtils.awaitTasks(tasks, tasks.size(), 120000l); // run in parallel with timeout } catch (Exception e) { - e.printStackTrace(); + log.error("Error awaiting tasks to complete: {}\n", e.getMessage(), e); } JsonFileManager.shutDownAllInstances(); @@ -177,8 +177,7 @@ public abstract class ExecutableForAppWithP2p extends HavenoExecutable { }, 1); } } catch (Throwable t) { - log.debug("App shutdown failed with exception"); - t.printStackTrace(); + log.info("App shutdown failed with exception: {}\n", t.getMessage(), t); PersistenceManager.flushAllDataToDiskAtShutdown(() -> { resultHandler.handleResult(); log.info("Graceful shutdown resulted in an error. Exiting now."); diff --git a/core/src/main/java/haveno/core/offer/OpenOfferManager.java b/core/src/main/java/haveno/core/offer/OpenOfferManager.java index aa561f127d..4824579dbf 100644 --- a/core/src/main/java/haveno/core/offer/OpenOfferManager.java +++ b/core/src/main/java/haveno/core/offer/OpenOfferManager.java @@ -977,7 +977,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe // handle result resultHandler.handleResult(null); } catch (Exception e) { - if (!openOffer.isCanceled()) e.printStackTrace(); + if (!openOffer.isCanceled()) log.error("Error processing pending offer: {}\n", e.getMessage(), e); errorMessageHandler.handleErrorMessage(e.getMessage()); } }).start(); @@ -1365,9 +1365,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe }); result = true; } catch (Exception e) { - e.printStackTrace(); errorMessage = "Exception at handleSignOfferRequest " + e.getMessage(); - log.error(errorMessage); + log.error(errorMessage + "\n", e); } finally { sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), result, errorMessage); } @@ -1519,8 +1518,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe result = true; } catch (Throwable t) { errorMessage = "Exception at handleRequestIsOfferAvailableMessage " + t.getMessage(); - log.error(errorMessage); - t.printStackTrace(); + log.error(errorMessage + "\n", t); } finally { sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), result, errorMessage); } diff --git a/core/src/main/java/haveno/core/provider/fee/FeeProvider.java b/core/src/main/java/haveno/core/provider/fee/FeeProvider.java index 30d140f83e..18838cee38 100644 --- a/core/src/main/java/haveno/core/provider/fee/FeeProvider.java +++ b/core/src/main/java/haveno/core/provider/fee/FeeProvider.java @@ -59,8 +59,7 @@ public class FeeProvider extends HttpClientProvider { map.put(Config.BTC_TX_FEE, btcTxFee); map.put(Config.BTC_MIN_TX_FEE, btcMinTxFee); } catch (Throwable t) { - log.error(t.toString()); - t.printStackTrace(); + log.error("Error getting fees: {}\n", t.getMessage(), t); } return new Tuple2<>(tsMap, map); } 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 871151a9e1..17bef33abb 100644 --- a/core/src/main/java/haveno/core/provider/price/PriceProvider.java +++ b/core/src/main/java/haveno/core/provider/price/PriceProvider.java @@ -68,8 +68,7 @@ public class PriceProvider extends HttpClientProvider { long timestampSec = MathUtils.doubleToLong((Double) treeMap.get("timestampSec")); marketPriceMap.put(currencyCode, new MarketPrice(currencyCode, price, timestampSec, true)); } catch (Throwable t) { - log.error(t.toString()); - t.printStackTrace(); + log.error("Error getting all prices: {}\n", t.getMessage(), t); } }); diff --git a/core/src/main/java/haveno/core/support/SupportManager.java b/core/src/main/java/haveno/core/support/SupportManager.java index d76154485d..10cbfdafaf 100644 --- a/core/src/main/java/haveno/core/support/SupportManager.java +++ b/core/src/main/java/haveno/core/support/SupportManager.java @@ -199,7 +199,7 @@ public abstract class SupportManager { if (dispute.isClosed()) dispute.reOpen(); trade.advanceDisputeState(Trade.DisputeState.DISPUTE_OPENED); } else if (dispute.isClosed()) { - trade.pollWalletNormallyForMs(30000); // sync to check for payout + trade.pollWalletNormallyForMs(60000); // sync to check for payout } } } 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 192516b59f..eb49e0c509 100644 --- a/core/src/main/java/haveno/core/support/dispute/DisputeManager.java +++ b/core/src/main/java/haveno/core/support/dispute/DisputeManager.java @@ -83,6 +83,9 @@ import monero.wallet.model.MoneroTxConfig; import monero.wallet.model.MoneroTxWallet; import javax.annotation.Nullable; + +import org.apache.commons.lang3.exception.ExceptionUtils; + import java.math.BigInteger; import java.security.KeyPair; import java.time.Instant; @@ -523,7 +526,7 @@ public abstract class DisputeManager> extends Sup DisputeValidation.validateSenderNodeAddress(dispute, message.getSenderNodeAddress(), config); //DisputeValidation.testIfDisputeTriesReplay(dispute, disputeList.getList()); } catch (DisputeValidation.ValidationException e) { - e.printStackTrace(); + log.error(ExceptionUtils.getStackTrace(e)); validationExceptions.add(e); throw e; } @@ -532,9 +535,9 @@ public abstract class DisputeManager> extends Sup try { DisputeValidation.validatePaymentAccountPayload(dispute); // TODO: add field to dispute details: valid, invalid, missing } catch (Exception e) { - e.printStackTrace(); - log.warn(e.getMessage()); + log.error(ExceptionUtils.getStackTrace(e)); trade.prependErrorMessage(e.getMessage()); + throw e; } // get sender @@ -606,9 +609,8 @@ public abstract class DisputeManager> extends Sup } } } catch (Exception e) { - e.printStackTrace(); + log.error(ExceptionUtils.getStackTrace(e)); errorMessage = e.getMessage(); - log.warn(errorMessage); if (trade != null) trade.setErrorMessage(errorMessage); } @@ -852,7 +854,7 @@ public abstract class DisputeManager> extends Sup // the state, as that is displayed to the user and we only persist that msg disputeResult.getChatMessage().setArrived(true); trade.advanceDisputeState(Trade.DisputeState.ARBITRATOR_SAW_ARRIVED_DISPUTE_CLOSED_MSG); - trade.pollWalletNormallyForMs(30000); + trade.pollWalletNormallyForMs(60000); requestPersistence(trade); resultHandler.handleResult(); } diff --git a/core/src/main/java/haveno/core/support/dispute/DisputeSummaryVerification.java b/core/src/main/java/haveno/core/support/dispute/DisputeSummaryVerification.java index a2f2f2f281..5fbd64dba5 100644 --- a/core/src/main/java/haveno/core/support/dispute/DisputeSummaryVerification.java +++ b/core/src/main/java/haveno/core/support/dispute/DisputeSummaryVerification.java @@ -71,7 +71,7 @@ public class DisputeSummaryVerification { disputeAgent = arbitratorManager.getDisputeAgentByNodeAddress(nodeAddress).orElse(null); checkNotNull(disputeAgent, "Dispute agent is null"); } catch (Throwable e) { - e.printStackTrace(); + log.error("Error verifying signature: {}\n", e.getMessage(), e); throw new IllegalArgumentException(Res.get("support.sigCheck.popup.invalidFormat")); } @@ -93,7 +93,7 @@ public class DisputeSummaryVerification { throw new IllegalArgumentException(Res.get("support.sigCheck.popup.failed")); } } catch (Throwable e) { - e.printStackTrace(); + log.error("Error verifying signature with agent pub key ring: {}\n", e.getMessage(), e); throw new IllegalArgumentException(Res.get("support.sigCheck.popup.invalidFormat")); } } 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 8082aad475..50be387c76 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 @@ -94,6 +94,8 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import org.apache.commons.lang3.exception.ExceptionUtils; + import static com.google.common.base.Preconditions.checkNotNull; @Slf4j @@ -355,11 +357,11 @@ public final class ArbitrationManager extends DisputeManager { + try { + + // get audio file + File wavFile = new File(havenoSetup.getConfig().appDataDir, fileName); + if (!wavFile.exists()) FileUtil.resourceToFile(fileName, wavFile); + AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(wavFile); + + // get original format + AudioFormat baseFormat = audioInputStream.getFormat(); + + // set target format: PCM_SIGNED, 16-bit + AudioFormat targetFormat = new AudioFormat( + AudioFormat.Encoding.PCM_SIGNED, + baseFormat.getSampleRate(), + 16, // 16-bit instead of 32-bit float + baseFormat.getChannels(), + baseFormat.getChannels() * 2, // Frame size: 2 bytes per channel (16-bit) + baseFormat.getSampleRate(), + false // Little-endian + ); + + // convert audio to target format + AudioInputStream convertedStream = AudioSystem.getAudioInputStream(targetFormat, audioInputStream); + + // play audio + DataLine.Info info = new DataLine.Info(SourceDataLine.class, targetFormat); + SourceDataLine sourceLine = (SourceDataLine) AudioSystem.getLine(info); + sourceLine.open(targetFormat); + sourceLine.start(); + byte[] buffer = new byte[1024]; + int bytesRead = 0; + while ((bytesRead = convertedStream.read(buffer, 0, buffer.length)) != -1) { + sourceLine.write(buffer, 0, bytesRead); + } + sourceLine.drain(); + sourceLine.close(); + convertedStream.close(); + audioInputStream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + }).start(); + } } diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index ca3df85358..5f51e8eadd 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -649,6 +649,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { ThreadUtils.submitToPool(() -> { if (newValue == Trade.Phase.DEPOSIT_REQUESTED) startPolling(); if (newValue == Trade.Phase.DEPOSITS_PUBLISHED) onDepositsPublished(); + if (newValue == Trade.Phase.PAYMENT_SENT) onPaymentSent(); if (isDepositsPublished() && !isPayoutUnlocked()) updatePollPeriod(); if (isPaymentReceived()) { UserThread.execute(() -> { @@ -999,8 +1000,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { xmrWalletService.deleteWallet(getWalletName()); xmrWalletService.deleteWalletBackups(getWalletName()); } catch (Exception e) { - log.warn(e.getMessage()); - e.printStackTrace(); + log.warn("Error deleting wallet for {} {}: {}\n", getClass().getSimpleName(), getId(), e.getMessage(), e); setErrorMessage(e.getMessage()); processModel.getTradeManager().getNotificationService().sendErrorNotification("Error", e.getMessage()); } @@ -1051,7 +1051,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { synchronized (HavenoUtils.getWalletFunctionLock()) { MoneroTxWallet tx = wallet.createTx(txConfig); exportMultisigHex(); - requestSaveWallet(); + saveWallet(); return tx; } } @@ -1152,14 +1152,14 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { throw e; } } - requestSaveWallet(); + saveWallet(); } log.info("Done importing multisig hexes for {} {} in {} ms, count={}", getClass().getSimpleName(), getShortId(), System.currentTimeMillis() - startTime, multisigHexes.size()); } private void handleWalletError(Exception e, MoneroRpcConnection sourceConnection) { if (HavenoUtils.isUnresponsive(e)) forceCloseWallet(); // wallet can be stuck a while - if (xmrConnectionService.isConnected()) requestSwitchToNextBestConnection(sourceConnection); + if (!HavenoUtils.isIllegal(e) && xmrConnectionService.isConnected()) requestSwitchToNextBestConnection(sourceConnection); getWallet(); // re-open wallet } @@ -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, tradeId={}, attempt={}/{}, error={}", getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage()); + log.warn("Failed to process payout tx, tradeId={}, attempt={}/{}, error={}", getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage(), e); handleWalletError(e, sourceConnection); if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e; HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying @@ -1351,20 +1351,20 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { try { MoneroMultisigSignResult result = wallet.signMultisigTxHex(payoutTxHex); if (result.getSignedMultisigTxHex() == null) throw new IllegalArgumentException("Error signing payout tx, signed multisig hex is null"); - payoutTxHex = result.getSignedMultisigTxHex(); - setPayoutTxHex(payoutTxHex); + setPayoutTxHex(result.getSignedMultisigTxHex()); } catch (Exception e) { throw new IllegalStateException(e); } // describe result - describedTxSet = wallet.describeMultisigTxSet(payoutTxHex); + describedTxSet = wallet.describeMultisigTxSet(getPayoutTxHex()); payoutTx = describedTxSet.getTxs().get(0); updatePayout(payoutTx); // verify fee is within tolerance by recreating payout tx // TODO (monero-project): creating tx will require exchanging updated multisig hex if message needs reprocessed. provide weight with describe_transfer so fee can be estimated? log.info("Creating fee estimate tx for {} {}", getClass().getSimpleName(), getId()); + saveWallet(); // save wallet before creating fee estimate tx MoneroTxWallet feeEstimateTx = createPayoutTx(); BigInteger feeEstimate = feeEstimateTx.getFee(); double feeDiff = payoutTx.getFee().subtract(feeEstimate).abs().doubleValue() / feeEstimate.doubleValue(); // TODO: use BigDecimal? @@ -1373,17 +1373,20 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { } // save trade state + saveWallet(); requestPersistence(); // submit payout tx - if (publish) { + boolean doPublish = publish && !isPayoutPublished(); + if (doPublish) { try { - wallet.submitMultisigTxHex(payoutTxHex); + wallet.submitMultisigTxHex(getPayoutTxHex()); setPayoutStatePublished(); } catch (Exception e) { - if (isPayoutPublished()) throw new IllegalStateException("Payout tx already published for " + getClass().getSimpleName() + " " + getShortId()); - if (HavenoUtils.isNotEnoughSigners(e)) throw new IllegalArgumentException(e); - throw new RuntimeException("Failed to submit payout tx for " + getClass().getSimpleName() + " " + getId(), e); + if (!isPayoutPublished()) { + if (HavenoUtils.isTransactionRejected(e) || HavenoUtils.isNotEnoughSigners(e)) throw new IllegalArgumentException(e); + throw new RuntimeException("Failed to submit payout tx for " + getClass().getSimpleName() + " " + getId() + ", error=" + e.getMessage(), e); + } } } } @@ -1536,8 +1539,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { try { ThreadUtils.awaitTask(shutDownTask, SHUTDOWN_TIMEOUT_MS); } catch (Exception e) { - log.warn("Error shutting down {} {}: {}", getClass().getSimpleName(), getId(), e.getMessage()); - e.printStackTrace(); + log.warn("Error shutting down {} {}: {}\n", getClass().getSimpleName(), getId(), e.getMessage(), e); // force close wallet forceCloseWallet(); @@ -2817,8 +2819,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { processing = false; if (!isInitialized || isShutDownStarted) return; if (isWalletConnectedToDaemon()) { - e.printStackTrace(); - log.warn("Error polling idle trade for {} {}: {}. Monerod={}", getClass().getSimpleName(), getId(), e.getMessage(), getXmrWalletService().getXmrConnectionService().getConnection()); + log.warn("Error polling idle trade for {} {}: {}. Monerod={}\n", getClass().getSimpleName(), getId(), e.getMessage(), getXmrWalletService().getXmrConnectionService().getConnection(), e); }; } }, getId()); @@ -2833,6 +2834,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { // close open offer or reset address entries if (this instanceof MakerTrade) { processModel.getOpenOfferManager().closeOpenOffer(getOffer()); + HavenoUtils.notificationService.sendTradeNotification(this, Phase.DEPOSITS_PUBLISHED, "Offer Taken", "Your offer " + offer.getId() + " has been accepted"); // TODO (woodser): use language translation } else { getXmrWalletService().resetAddressEntriesForOpenOffer(getId()); } @@ -2841,6 +2843,12 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model { ThreadUtils.submitToPool(() -> xmrWalletService.freezeOutputs(getSelf().getReserveTxKeyImages())); } + private void onPaymentSent() { + if (this instanceof SellerTrade) { + HavenoUtils.notificationService.sendTradeNotification(this, Phase.PAYMENT_SENT, "Payment Sent", "The buyer has sent the payment"); // TODO (woodser): use language translation + } + } + /////////////////////////////////////////////////////////////////////////////////////////// // PROTO BUFFER /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/haveno/core/trade/TradeManager.java b/core/src/main/java/haveno/core/trade/TradeManager.java index b88330a3ab..55acf63d87 100644 --- a/core/src/main/java/haveno/core/trade/TradeManager.java +++ b/core/src/main/java/haveno/core/trade/TradeManager.java @@ -68,7 +68,6 @@ import haveno.core.support.dispute.mediation.mediator.MediatorManager; import haveno.core.support.dispute.messages.DisputeClosedMessage; import haveno.core.support.dispute.messages.DisputeOpenedMessage; import haveno.core.trade.Trade.DisputeState; -import haveno.core.trade.Trade.Phase; import haveno.core.trade.failed.FailedTradesManager; import haveno.core.trade.handlers.TradeResultHandler; import haveno.core.trade.messages.DepositRequest; @@ -134,7 +133,6 @@ import lombok.Setter; import monero.daemon.model.MoneroTx; import org.bitcoinj.core.Coin; import org.bouncycastle.crypto.params.KeyParameter; -import org.fxmisc.easybind.EasyBind; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -258,7 +256,9 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi failedTradesManager.setUnFailTradeCallback(this::unFailTrade); - xmrWalletService.setTradeManager(this); + // TODO: better way to set references + xmrWalletService.setTradeManager(this); // TODO: set reference in HavenoUtils for consistency + HavenoUtils.notificationService = notificationService; } @@ -366,8 +366,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi trade.onShutDownStarted(); } catch (Exception e) { if (e.getMessage() != null && e.getMessage().contains("Connection reset")) return; // expected if shut down with ctrl+c - log.warn("Error notifying {} {} that shut down started {}", trade.getClass().getSimpleName(), trade.getId()); - e.printStackTrace(); + log.warn("Error notifying {} {} that shut down started: {}\n", trade.getClass().getSimpleName(), trade.getId(), e.getMessage(), e); } }); try { @@ -396,15 +395,13 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi trade.shutDown(); } catch (Exception e) { if (e.getMessage() != null && (e.getMessage().contains("Connection reset") || e.getMessage().contains("Connection refused"))) return; // expected if shut down with ctrl+c - log.warn("Error closing {} {}", trade.getClass().getSimpleName(), trade.getId()); - e.printStackTrace(); + log.warn("Error closing {} {}: {}", trade.getClass().getSimpleName(), trade.getId(), e.getMessage(), e); } }); try { ThreadUtils.awaitTasks(tasks); } catch (Exception e) { - log.warn("Error shutting down trades: {}", e.getMessage()); - e.printStackTrace(); + log.warn("Error shutting down trades: {}\n", e.getMessage(), e); } } @@ -462,8 +459,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi } } catch (Exception e) { if (!isShutDownStarted) { - e.printStackTrace(); - log.warn("Error initializing {} {}: {}", trade.getClass().getSimpleName(), trade.getId(), e.getMessage()); + log.warn("Error initializing {} {}: {}\n", trade.getClass().getSimpleName(), trade.getId(), e.getMessage(), e); trade.setInitError(e); } } @@ -603,14 +599,6 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi initTradeAndProtocol(trade, createTradeProtocol(trade)); addTrade(trade); - // notify on phase changes - // TODO (woodser): save subscription, bind on startup - EasyBind.subscribe(trade.statePhaseProperty(), phase -> { - if (phase == Phase.DEPOSITS_PUBLISHED) { - notificationService.sendTradeNotification(trade, "Offer Taken", "Your offer " + offer.getId() + " has been accepted"); // TODO (woodser): use language translation - } - }); - // process with protocol ((MakerProtocol) getTradeProtocol(trade)).handleInitTradeRequest(request, sender, errorMessage -> { log.warn("Maker error during trade initialization: " + errorMessage); diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java b/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java index a97beb7d61..54ced82510 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java @@ -68,7 +68,7 @@ public class ArbitratorProcessDepositRequest extends TradeTask { complete(); } catch (Throwable t) { this.error = t; - t.printStackTrace(); + log.error("Error processing deposit request for trade {}: {}\n", trade.getId(), t.getMessage(), t); trade.setStateIfValidTransitionTo(Trade.State.PUBLISH_DEPOSIT_TX_REQUEST_FAILED); failed(t); } @@ -155,15 +155,14 @@ public class ArbitratorProcessDepositRequest extends TradeTask { log.info("Arbitrator published deposit txs for trade " + trade.getId()); trade.setStateIfValidTransitionTo(Trade.State.ARBITRATOR_PUBLISHED_DEPOSIT_TXS); } catch (Exception e) { - log.warn("Arbitrator error publishing deposit txs for trade {} {}: {}", trade.getClass().getSimpleName(), trade.getShortId(), e.getMessage()); - e.printStackTrace(); + log.warn("Arbitrator error publishing deposit txs for trade {} {}: {}\n", trade.getClass().getSimpleName(), trade.getShortId(), e.getMessage(), e); if (!depositTxsRelayed) { // flush txs from pool try { daemon.flushTxPool(processModel.getMaker().getDepositTxHash(), processModel.getTaker().getDepositTxHash()); } catch (Exception e2) { - e2.printStackTrace(); + log.warn("Error flushing deposit txs from pool for trade {}: {}\n", trade.getId(), e2.getMessage(), e2); } } throw e; diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessReserveTx.java b/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessReserveTx.java index fff1192ce8..10baac8567 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessReserveTx.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/ArbitratorProcessReserveTx.java @@ -29,6 +29,8 @@ import monero.daemon.model.MoneroTx; import java.math.BigInteger; +import org.apache.commons.lang3.exception.ExceptionUtils; + /** * Arbitrator verifies reserve tx from maker or taker. * @@ -73,7 +75,7 @@ public class ArbitratorProcessReserveTx extends TradeTask { request.getReserveTxKey(), null); } catch (Exception e) { - e.printStackTrace(); + log.error(ExceptionUtils.getStackTrace(e)); throw new RuntimeException("Error processing reserve tx from " + (isFromMaker ? "maker " : "taker ") + processModel.getTempTradePeerNodeAddress() + ", offerId=" + offer.getId() + ": " + e.getMessage()); } diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/MaybeResendDisputeClosedMessageWithPayout.java b/core/src/main/java/haveno/core/trade/protocol/tasks/MaybeResendDisputeClosedMessageWithPayout.java index 30c3e3d816..4dd914cbe4 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/MaybeResendDisputeClosedMessageWithPayout.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/MaybeResendDisputeClosedMessageWithPayout.java @@ -62,7 +62,7 @@ public class MaybeResendDisputeClosedMessageWithPayout extends TradeTask { HavenoUtils.arbitrationManager.closeDisputeTicket(dispute.getDisputeResultProperty().get(), dispute, dispute.getDisputeResultProperty().get().summaryNotesProperty().get(), () -> { completeAux(); }, (errMessage, err) -> { - err.printStackTrace(); + log.error("Failed to close dispute ticket for trade {}: {}\n", trade.getId(), errMessage, err); failed(err); }); ticketClosed = true; diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessDepositsConfirmedMessage.java b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessDepositsConfirmedMessage.java index a43c667d22..c11df74fae 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessDepositsConfirmedMessage.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessDepositsConfirmedMessage.java @@ -70,7 +70,7 @@ public class ProcessDepositsConfirmedMessage extends TradeTask { try { trade.importMultisigHex(); } catch (Exception e) { - e.printStackTrace(); + log.warn("Error importing multisig hex on deposits confirmed for trade " + trade.getId() + ": " + e.getMessage() + "\n", e); } }); } diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessInitMultisigRequest.java b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessInitMultisigRequest.java index 78fafed62b..71b53c5dc5 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessInitMultisigRequest.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessInitMultisigRequest.java @@ -32,6 +32,7 @@ import haveno.network.p2p.NodeAddress; import haveno.network.p2p.SendDirectMessageListener; import lombok.extern.slf4j.Slf4j; import monero.wallet.MoneroWallet; +import monero.wallet.model.MoneroMultisigInfo; import monero.wallet.model.MoneroMultisigInitResult; import java.util.Arrays; @@ -118,8 +119,17 @@ public class ProcessInitMultisigRequest extends TradeTask { if (processModel.getMultisigAddress() == null && peers[0].getExchangedMultisigHex() != null && peers[1].getExchangedMultisigHex() != null) { log.info("Importing exchanged multisig hex for trade {}", trade.getId()); MoneroMultisigInitResult result = multisigWallet.exchangeMultisigKeys(Arrays.asList(peers[0].getExchangedMultisigHex(), peers[1].getExchangedMultisigHex()), xmrWalletService.getWalletPassword()); + + // check multisig state + MoneroMultisigInfo multisigInfo = multisigWallet.getMultisigInfo(); + if (!multisigInfo.isMultisig()) throw new RuntimeException("Multisig wallet is not multisig on completion"); + if (!multisigInfo.isReady()) throw new RuntimeException("Multisig wallet is not ready on completion"); + if (multisigInfo.getThreshold() != 2) throw new RuntimeException("Multisig wallet has unexpected threshold: " + multisigInfo.getThreshold()); + if (multisigInfo.getNumParticipants() != 3) throw new RuntimeException("Multisig wallet has unexpected number of participants: " + multisigInfo.getNumParticipants()); + + // set final address and save processModel.setMultisigAddress(result.getAddress()); - new Thread(() -> trade.saveWallet()).start(); // save multisig wallet off thread on completion + trade.saveWallet(); trade.setStateIfValidTransitionTo(Trade.State.MULTISIG_COMPLETED); } 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 d8de68dab1..03ff6bd0d3 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 @@ -120,7 +120,7 @@ public class ProcessPaymentReceivedMessage extends TradeTask { } catch (Throwable t) { // do not reprocess illegal argument - if (t instanceof IllegalArgumentException) { + if (HavenoUtils.isIllegal(t)) { trade.getSeller().setPaymentReceivedMessage(null); // do not reprocess trade.requestPersistence(); } @@ -151,6 +151,7 @@ public class ProcessPaymentReceivedMessage extends TradeTask { 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); for (int i = 0; i < 5; i++) { if (trade.isPayoutPublished()) break; HavenoUtils.waitFor(Trade.DEFER_PUBLISH_MS / 5); 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 bf0a4df025..a483026a6e 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 @@ -65,10 +65,10 @@ public class SellerPreparePaymentReceivedMessage extends TradeTask { 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", trade.getClass().getSimpleName(), trade.getId(), e.getMessage()); + 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()); + log.warn("Error verifying, signing, and publishing payout tx for trade {}: {}", trade.getId(), e.getMessage(), e); throw e; } } diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/TradeTask.java b/core/src/main/java/haveno/core/trade/protocol/tasks/TradeTask.java index 231e54824a..293b74f99d 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/TradeTask.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/TradeTask.java @@ -61,7 +61,7 @@ public abstract class TradeTask extends Task { @Override protected void failed(Throwable t) { - t.printStackTrace(); + log.error("Trade task failed, error={}\n", t.getMessage(), t); appendExceptionToErrorMessage(t); trade.setErrorMessage(errorMessage); processModel.getTradeManager().requestPersistence(); diff --git a/core/src/main/java/haveno/core/user/Preferences.java b/core/src/main/java/haveno/core/user/Preferences.java index 311bd3108b..b57b5708ee 100644 --- a/core/src/main/java/haveno/core/user/Preferences.java +++ b/core/src/main/java/haveno/core/user/Preferences.java @@ -132,6 +132,8 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid private final String xmrNodesFromOptions; @Getter private final BooleanProperty useStandbyModeProperty = new SimpleBooleanProperty(prefPayload.isUseStandbyMode()); + @Getter + private final BooleanProperty useSoundForNotificationsProperty = new SimpleBooleanProperty(prefPayload.isUseSoundForNotifications()); /////////////////////////////////////////////////////////////////////////////////////////// // Constructor @@ -162,6 +164,11 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid requestPersistence(); }); + useSoundForNotificationsProperty.addListener((ov) -> { + prefPayload.setUseSoundForNotifications(useSoundForNotificationsProperty.get()); + requestPersistence(); + }); + traditionalCurrenciesAsObservable.addListener((javafx.beans.Observable ov) -> { prefPayload.getTraditionalCurrencies().clear(); prefPayload.getTraditionalCurrencies().addAll(traditionalCurrenciesAsObservable); @@ -259,6 +266,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid // set all properties useAnimationsProperty.set(prefPayload.isUseAnimations()); useStandbyModeProperty.set(prefPayload.isUseStandbyMode()); + useSoundForNotificationsProperty.set(prefPayload.isUseSoundForNotifications()); cssThemeProperty.set(prefPayload.getCssTheme()); @@ -697,6 +705,10 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid this.useStandbyModeProperty.set(useStandbyMode); } + public void setUseSoundForNotifications(boolean useSoundForNotifications) { + this.useSoundForNotificationsProperty.set(useSoundForNotifications); + } + public void setTakeOfferSelectedPaymentAccountId(String value) { prefPayload.setTakeOfferSelectedPaymentAccountId(value); requestPersistence(); @@ -946,6 +958,8 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid void setUseStandbyMode(boolean useStandbyMode); + void setUseSoundForNotifications(boolean useSoundForNotifications); + void setTakeOfferSelectedPaymentAccountId(String value); void setIgnoreDustThreshold(int value); diff --git a/core/src/main/java/haveno/core/user/PreferencesPayload.java b/core/src/main/java/haveno/core/user/PreferencesPayload.java index b0a2fd7d4a..5484c514ea 100644 --- a/core/src/main/java/haveno/core/user/PreferencesPayload.java +++ b/core/src/main/java/haveno/core/user/PreferencesPayload.java @@ -108,6 +108,7 @@ public final class PreferencesPayload implements PersistableEnvelope { private boolean useMarketNotifications = true; private boolean usePriceNotifications = true; private boolean useStandbyMode = false; + private boolean useSoundForNotifications = true; @Nullable private String rpcUser; @Nullable @@ -185,6 +186,7 @@ public final class PreferencesPayload implements PersistableEnvelope { .setUseMarketNotifications(useMarketNotifications) .setUsePriceNotifications(usePriceNotifications) .setUseStandbyMode(useStandbyMode) + .setUseSoundForNotifications(useSoundForNotifications) .setBuyerSecurityDepositAsPercent(buyerSecurityDepositAsPercent) .setIgnoreDustThreshold(ignoreDustThreshold) .setClearDataAfterDays(clearDataAfterDays) @@ -280,6 +282,7 @@ public final class PreferencesPayload implements PersistableEnvelope { proto.getUseMarketNotifications(), proto.getUsePriceNotifications(), proto.getUseStandbyMode(), + proto.getUseSoundForNotifications(), proto.getRpcUser().isEmpty() ? null : proto.getRpcUser(), proto.getRpcPw().isEmpty() ? null : proto.getRpcPw(), proto.getTakeOfferSelectedPaymentAccountId().isEmpty() ? null : proto.getTakeOfferSelectedPaymentAccountId(), diff --git a/core/src/main/java/haveno/core/xmr/Balances.java b/core/src/main/java/haveno/core/xmr/Balances.java index 6958c8288c..fe49b941fd 100644 --- a/core/src/main/java/haveno/core/xmr/Balances.java +++ b/core/src/main/java/haveno/core/xmr/Balances.java @@ -111,6 +111,7 @@ public class Balances { public XmrBalanceInfo getBalances() { synchronized (this) { + if (availableBalance == null) return null; return new XmrBalanceInfo(availableBalance.longValue() + pendingBalance.longValue(), availableBalance.longValue(), pendingBalance.longValue(), @@ -127,6 +128,9 @@ public class Balances { synchronized (this) { synchronized (HavenoUtils.xmrWalletService.getWalletLock()) { + // get non-trade balance before + BigInteger balanceSumBefore = getNonTradeBalanceSum(); + // get wallet balances BigInteger balance = xmrWalletService.getWallet() == null ? BigInteger.ZERO : xmrWalletService.getBalance(); availableBalance = xmrWalletService.getWallet() == null ? BigInteger.ZERO : xmrWalletService.getAvailableBalance(); @@ -160,8 +164,25 @@ public class Balances { reservedBalance = reservedOfferBalance.add(reservedTradeBalance); // notify balance update - UserThread.execute(() -> updateCounter.set(updateCounter.get() + 1)); + UserThread.execute(() -> { + + // check if funds received + boolean fundsReceived = balanceSumBefore != null && getNonTradeBalanceSum().compareTo(balanceSumBefore) > 0; + if (fundsReceived) { + HavenoUtils.playCashRegisterSound(); + } + + // increase counter to notify listeners + updateCounter.set(updateCounter.get() + 1); + }); } } } + + private BigInteger getNonTradeBalanceSum() { + synchronized (this) { + if (availableBalance == null) return null; + return availableBalance.add(pendingBalance).add(reservedOfferBalance); + } + } } diff --git a/core/src/main/java/haveno/core/xmr/XmrNodeSettings.java b/core/src/main/java/haveno/core/xmr/XmrNodeSettings.java index 0db9868f04..9802f036c3 100644 --- a/core/src/main/java/haveno/core/xmr/XmrNodeSettings.java +++ b/core/src/main/java/haveno/core/xmr/XmrNodeSettings.java @@ -35,6 +35,8 @@ public class XmrNodeSettings implements PersistableEnvelope { String bootstrapUrl; @Nullable List startupFlags; + @Nullable + Boolean syncBlockchain; public XmrNodeSettings() { } @@ -43,7 +45,8 @@ public class XmrNodeSettings implements PersistableEnvelope { return new XmrNodeSettings( proto.getBlockchainPath(), proto.getBootstrapUrl(), - proto.getStartupFlagsList()); + proto.getStartupFlagsList(), + proto.getSyncBlockchain()); } @Override @@ -52,6 +55,7 @@ public class XmrNodeSettings implements PersistableEnvelope { Optional.ofNullable(blockchainPath).ifPresent(e -> builder.setBlockchainPath(blockchainPath)); Optional.ofNullable(bootstrapUrl).ifPresent(e -> builder.setBootstrapUrl(bootstrapUrl)); Optional.ofNullable(startupFlags).ifPresent(e -> builder.addAllStartupFlags(startupFlags)); + Optional.ofNullable(syncBlockchain).ifPresent(e -> builder.setSyncBlockchain(syncBlockchain)); return builder.build(); } } diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletBase.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletBase.java index 0cbc41b234..877eb387a2 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletBase.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletBase.java @@ -6,6 +6,8 @@ import java.util.Optional; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import org.apache.commons.lang3.exception.ExceptionUtils; + import haveno.common.Timer; import haveno.common.UserThread; import haveno.core.api.XmrConnectionService; @@ -106,7 +108,7 @@ public class XmrWalletBase { height = wallet.getHeight(); // can get read timeout while syncing } catch (Exception e) { log.warn("Error getting wallet height while syncing with progress: " + e.getMessage()); - if (wallet != null && !isShutDownStarted) e.printStackTrace(); + if (wallet != null && !isShutDownStarted) log.warn(ExceptionUtils.getStackTrace(e)); // stop polling and release latch syncProgressError = e; 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 55b09682bd..1a9ead3597 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -818,7 +818,7 @@ public class XmrWalletService extends XmrWalletBase { MoneroFeeEstimate feeEstimates = getDaemon().getFeeEstimate(); BigInteger baseFeeEstimate = feeEstimates.getFees().get(2); // get elevated fee per kB BigInteger qmask = feeEstimates.getQuantizationMask(); - log.info("Monero base fee estimate={}, qmask={}: " + baseFeeEstimate, qmask); + log.info("Monero base fee estimate={}, qmask={}", baseFeeEstimate, qmask); // get tx base fee BigInteger baseFee = baseFeeEstimate.multiply(BigInteger.valueOf(txWeight)); @@ -922,8 +922,7 @@ public class XmrWalletService extends XmrWalletBase { try { ThreadUtils.awaitTask(shutDownTask, SHUTDOWN_TIMEOUT_MS); } catch (Exception e) { - log.warn("Error shutting down {}: {}", getClass().getSimpleName(), e.getMessage()); - e.printStackTrace(); + log.warn("Error shutting down {}: {}\n", getClass().getSimpleName(), e.getMessage(), e); // force close wallet forceCloseMainWallet(); @@ -945,8 +944,7 @@ public class XmrWalletService extends XmrWalletBase { List unusedAddressEntries = getUnusedAddressEntries(); if (!unusedAddressEntries.isEmpty()) return xmrAddressEntryList.swapAvailableToAddressEntryWithOfferId(unusedAddressEntries.get(0), context, offerId); } catch (Exception e) { - log.warn("Error getting new address entry based on incoming transactions"); - e.printStackTrace(); + log.warn("Error getting new address entry based on incoming transactions: {}\n", e.getMessage(), e); } // create new entry @@ -1172,8 +1170,7 @@ public class XmrWalletService extends XmrWalletBase { try { balanceListener.onBalanceChanged(balance); } catch (Exception e) { - log.warn("Failed to notify balance listener of change"); - e.printStackTrace(); + log.warn("Failed to notify balance listener of change: {}\n", e.getMessage(), e); } }); } @@ -1309,8 +1306,7 @@ public class XmrWalletService extends XmrWalletBase { try { doMaybeInitMainWallet(sync, MAX_SYNC_ATTEMPTS); } catch (Exception e) { - log.warn("Error initializing main wallet: " + e.getMessage()); - e.printStackTrace(); + log.warn("Error initializing main wallet: {}\n", e.getMessage(), e); HavenoUtils.setTopError(e.getMessage()); throw e; } @@ -1459,9 +1455,10 @@ public class XmrWalletService extends XmrWalletBase { log.info("Done creating full wallet " + config.getPath() + " in " + (System.currentTimeMillis() - time) + " ms"); return walletFull; } catch (Exception e) { - e.printStackTrace(); + String errorMsg = "Could not create wallet '" + config.getPath() + "': " + e.getMessage(); + log.warn(errorMsg + "\n", e); if (walletFull != null) forceCloseWallet(walletFull, config.getPath()); - throw new IllegalStateException("Could not create wallet '" + config.getPath() + "'"); + throw new IllegalStateException(errorMsg); } } @@ -1525,9 +1522,10 @@ public class XmrWalletService extends XmrWalletBase { log.info("Done opening full wallet " + config.getPath()); return walletFull; } catch (Exception e) { - e.printStackTrace(); + String errorMsg = "Could not open full wallet '" + config.getPath() + "': " + e.getMessage(); + log.warn(errorMsg + "\n", e); if (walletFull != null) forceCloseWallet(walletFull, config.getPath()); - throw new IllegalStateException("Could not open full wallet '" + config.getPath() + "'"); + throw new IllegalStateException(errorMsg); } } @@ -1557,7 +1555,7 @@ public class XmrWalletService extends XmrWalletBase { log.info("Done creating RPC wallet " + config.getPath() + " in " + (System.currentTimeMillis() - time) + " ms"); return walletRpc; } catch (Exception e) { - e.printStackTrace(); + log.warn("Could not create wallet '" + config.getPath() + "': " + e.getMessage() + "\n", e); 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 in your task manager, and restart Haveno."); } @@ -1629,7 +1627,7 @@ public class XmrWalletService extends XmrWalletBase { log.info("Done opening RPC wallet " + config.getPath()); return walletRpc; } catch (Exception e) { - e.printStackTrace(); + log.warn("Could not open wallet '" + config.getPath() + "': " + e.getMessage() + "\n", e); 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 in your task manager, and restart Haveno.\n\nError message: " + e.getMessage()); } @@ -1733,7 +1731,7 @@ public class XmrWalletService extends XmrWalletBase { wallet.changePassword(oldPassword, newPassword); saveMainWallet(); } catch (Exception e) { - e.printStackTrace(); + log.warn("Error changing main wallet password: " + e.getMessage() + "\n", e); throw e; } }); @@ -1916,7 +1914,7 @@ public class XmrWalletService extends XmrWalletBase { cacheWalletInfo(); requestSaveMainWallet(); } catch (Exception e) { - e.printStackTrace(); + log.warn("Error caching wallet info: " + e.getMessage() + "\n", e); } } } diff --git a/core/src/main/resources/cash_register.wav b/core/src/main/resources/cash_register.wav new file mode 100644 index 0000000000..c11d914655 Binary files /dev/null and b/core/src/main/resources/cash_register.wav differ diff --git a/core/src/main/resources/chime.wav b/core/src/main/resources/chime.wav new file mode 100644 index 0000000000..c2ba1ab089 Binary files /dev/null and b/core/src/main/resources/chime.wav differ diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index a38f13bfec..8601e3206e 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -1266,6 +1266,7 @@ setting.preferences.general=General preferences setting.preferences.explorer=Monero Explorer setting.preferences.deviation=Max. deviation from market price setting.preferences.avoidStandbyMode=Avoid standby mode +setting.preferences.useSoundForNotifications=Play sounds for notifications setting.preferences.autoConfirmXMR=XMR auto-confirm setting.preferences.autoConfirmEnabled=Enabled setting.preferences.autoConfirmRequiredConfirmations=Required confirmations diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index 3849693d1b..a3edc2ec22 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -987,6 +987,7 @@ setting.preferences.general=Základní nastavení setting.preferences.explorer=Monero Explorer setting.preferences.deviation=Max. odchylka od tržní ceny setting.preferences.avoidStandbyMode=Vyhněte se pohotovostnímu režimu +setting.preferences.useSoundForNotifications=Přehrávat zvuky pro upozornění setting.preferences.autoConfirmXMR=Automatické potvrzení XMR setting.preferences.autoConfirmEnabled=Povoleno setting.preferences.autoConfirmRequiredConfirmations=Požadovaná potvrzení diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index 93a8edb198..1dc5d74051 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -987,6 +987,7 @@ setting.preferences.general=Allgemeine Voreinstellungen setting.preferences.explorer=Monero Explorer setting.preferences.deviation=Max. Abweichung vom Marktpreis setting.preferences.avoidStandbyMode=Standby Modus verhindern +setting.preferences.useSoundForNotifications=Spiele Geräusche für Benachrichtigungen setting.preferences.autoConfirmXMR=XMR automatische Bestätigung setting.preferences.autoConfirmEnabled=Aktiviert setting.preferences.autoConfirmRequiredConfirmations=Benötigte Bestätigungen diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index bd4ee417a2..a3af6a0fd1 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -988,6 +988,7 @@ setting.preferences.general=Preferencias generales setting.preferences.explorer=Explorador Monero setting.preferences.deviation=Desviación máxima del precio de mercado setting.preferences.setting.preferences.avoidStandbyMode=Evitar modo en espera +setting.preferences.useSoundForNotifications=Reproducir sonidos para notificaciones setting.preferences.autoConfirmXMR=Autoconfirmación XMR setting.preferences.autoConfirmEnabled=Habilitado setting.preferences.autoConfirmRequiredConfirmations=Confirmaciones requeridas diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index eaf7147ae2..b0dd076441 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -984,6 +984,7 @@ setting.preferences.general=اولویت‌های عمومی setting.preferences.explorer=Monero Explorer setting.preferences.deviation=حداکثر تفاوت از قیمت روز بازار setting.preferences.avoidStandbyMode=حالت «آماده باش» را نادیده بگیر +setting.preferences.useSoundForNotifications=پخش صداها برای اعلان‌ها setting.preferences.autoConfirmXMR=XMR auto-confirm setting.preferences.autoConfirmEnabled=Enabled setting.preferences.autoConfirmRequiredConfirmations=Required confirmations diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index 8d3b4ce468..901d3a1956 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -989,6 +989,7 @@ setting.preferences.general=Préférences générales setting.preferences.explorer=Exploreur Monero setting.preferences.deviation=Ecart maximal par rapport au prix du marché setting.preferences.avoidStandbyMode=Éviter le mode veille +setting.preferences.useSoundForNotifications=Jouer des sons pour les notifications setting.preferences.autoConfirmXMR=Auto-confirmation XMR setting.preferences.autoConfirmEnabled=Activé setting.preferences.autoConfirmRequiredConfirmations=Confirmations requises diff --git a/core/src/main/resources/i18n/displayStrings_it.properties b/core/src/main/resources/i18n/displayStrings_it.properties index 044873db1a..8eb8e15938 100644 --- a/core/src/main/resources/i18n/displayStrings_it.properties +++ b/core/src/main/resources/i18n/displayStrings_it.properties @@ -986,6 +986,7 @@ setting.preferences.general=Preferenze generali setting.preferences.explorer=Monero Explorer setting.preferences.deviation=Deviazione massima del prezzo di mercato setting.preferences.avoidStandbyMode=Evita modalità standby +setting.preferences.useSoundForNotifications=Riproduci suoni per le notifiche setting.preferences.autoConfirmXMR=XMR auto-confirm setting.preferences.autoConfirmEnabled=Enabled setting.preferences.autoConfirmRequiredConfirmations=Required confirmations diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index a0a71e4c1d..8f187cff65 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -987,6 +987,7 @@ setting.preferences.general=一般設定 setting.preferences.explorer=ビットコインのエクスプローラ setting.preferences.deviation=市場価格からの最大偏差 setting.preferences.avoidStandbyMode=スタンバイモードを避ける +setting.preferences.useSoundForNotifications=通知音の再生 setting.preferences.autoConfirmXMR=XMR自動確認 setting.preferences.autoConfirmEnabled=有効されました setting.preferences.autoConfirmRequiredConfirmations=必要承認 diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index 177f3dc674..19d7498f42 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -988,6 +988,7 @@ setting.preferences.general=Preferências gerais setting.preferences.explorer=Monero Explorer setting.preferences.deviation=Desvio máx. do preço do mercado setting.preferences.avoidStandbyMode=Impedir modo de economia de energia +setting.preferences.useSoundForNotifications=Reproduzir sons para notificações setting.preferences.autoConfirmXMR=XMR auto-confirm setting.preferences.autoConfirmEnabled=Enabled setting.preferences.autoConfirmRequiredConfirmations=Required confirmations diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index 6841f97cbd..a14e3c7b68 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -985,6 +985,7 @@ setting.preferences.general=Preferências gerais setting.preferences.explorer=Monero Explorer setting.preferences.deviation=Máx. desvio do preço de mercado setting.preferences.avoidStandbyMode=Evite o modo espera +setting.preferences.useSoundForNotifications=Reproduzir sons para notificações setting.preferences.autoConfirmXMR=XMR auto-confirm setting.preferences.autoConfirmEnabled=Enabled setting.preferences.autoConfirmRequiredConfirmations=Required confirmations diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index ccdcd4d6fb..346053e103 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -984,6 +984,7 @@ setting.preferences.general=Основные настройки setting.preferences.explorer=Monero Explorer setting.preferences.deviation=Макс. отклонение от рыночного курса setting.preferences.avoidStandbyMode=Избегать режима ожидания +setting.preferences.useSoundForNotifications=Воспроизводить звуки для уведомлений setting.preferences.autoConfirmXMR=XMR auto-confirm setting.preferences.autoConfirmEnabled=Enabled setting.preferences.autoConfirmRequiredConfirmations=Required confirmations diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index b23765c34a..626916d17e 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -984,6 +984,7 @@ setting.preferences.general=การตั้งค่าทั่วไป setting.preferences.explorer=Monero Explorer setting.preferences.deviation=สูงสุด ส่วนเบี่ยงเบนจากราคาตลาด setting.preferences.avoidStandbyMode=หลีกเลี่ยงโหมดแสตนบายด์ +setting.preferences.useSoundForNotifications=เล่นเสียงสำหรับการแจ้งเตือน setting.preferences.autoConfirmXMR=XMR auto-confirm setting.preferences.autoConfirmEnabled=Enabled setting.preferences.autoConfirmRequiredConfirmations=Required confirmations diff --git a/core/src/main/resources/i18n/displayStrings_tr.properties b/core/src/main/resources/i18n/displayStrings_tr.properties index f7732593ad..73304adac6 100644 --- a/core/src/main/resources/i18n/displayStrings_tr.properties +++ b/core/src/main/resources/i18n/displayStrings_tr.properties @@ -1261,6 +1261,7 @@ setting.preferences.general=Genel tercihler setting.preferences.explorer=Monero Gezgini setting.preferences.deviation=Piyasa fiyatından maksimum sapma setting.preferences.avoidStandbyMode=Bekleme modundan kaçın +setting.preferences.useSoundForNotifications=Bildirimler için sesleri çal setting.preferences.autoConfirmXMR=XMR otomatik onay setting.preferences.autoConfirmEnabled=Etkin setting.preferences.autoConfirmRequiredConfirmations=Gerekli onaylar diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index a1978b79cc..ca3f46aaeb 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -986,6 +986,7 @@ setting.preferences.general=Tham khảo chung setting.preferences.explorer=Monero Explorer setting.preferences.deviation=Sai lệch tối đa so với giá thị trường setting.preferences.avoidStandbyMode=Tránh để chế độ chờ +setting.preferences.useSoundForNotifications=Phát âm thanh cho thông báo setting.preferences.autoConfirmXMR=XMR auto-confirm setting.preferences.autoConfirmEnabled=Enabled setting.preferences.autoConfirmRequiredConfirmations=Required confirmations diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index e809a3104c..c9dce9d751 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -987,6 +987,7 @@ setting.preferences.general=通用偏好 setting.preferences.explorer=比特币区块浏览器 setting.preferences.deviation=与市场价格最大差价 setting.preferences.avoidStandbyMode=避免待机模式 +setting.preferences.useSoundForNotifications=播放通知声音 setting.preferences.autoConfirmXMR=XMR 自动确认 setting.preferences.autoConfirmEnabled=启用 setting.preferences.autoConfirmRequiredConfirmations=已要求确认 diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index 435da337a2..53ee27d269 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -987,6 +987,7 @@ setting.preferences.general=通用偏好 setting.preferences.explorer=比特幣區塊瀏覽器 setting.preferences.deviation=與市場價格最大差價 setting.preferences.avoidStandbyMode=避免待機模式 +setting.preferences.useSoundForNotifications=播放通知音效 setting.preferences.autoConfirmXMR=XMR 自動確認 setting.preferences.autoConfirmEnabled=啟用 setting.preferences.autoConfirmRequiredConfirmations=已要求確認 diff --git a/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsDataModel.java b/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsDataModel.java index d6b937ac4b..95fa4bbd63 100644 --- a/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsDataModel.java +++ b/desktop/src/main/java/haveno/desktop/main/account/content/cryptoaccounts/CryptoAccountsDataModel.java @@ -18,7 +18,6 @@ package haveno.desktop.main.account.content.cryptoaccounts; import com.google.inject.Inject; -import haveno.common.crypto.KeyRing; import haveno.common.file.CorruptedStorageFileHandler; import haveno.common.proto.persistable.PersistenceProtoResolver; import haveno.core.account.witness.AccountAgeWitnessService; @@ -55,7 +54,6 @@ class CryptoAccountsDataModel extends ActivatableDataModel { private final String accountsFileName = "CryptoPaymentAccounts"; private final PersistenceProtoResolver persistenceProtoResolver; private final CorruptedStorageFileHandler corruptedStorageFileHandler; - private final KeyRing keyRing; @Inject public CryptoAccountsDataModel(User user, @@ -64,8 +62,7 @@ class CryptoAccountsDataModel extends ActivatableDataModel { TradeManager tradeManager, AccountAgeWitnessService accountAgeWitnessService, PersistenceProtoResolver persistenceProtoResolver, - CorruptedStorageFileHandler corruptedStorageFileHandler, - KeyRing keyRing) { + CorruptedStorageFileHandler corruptedStorageFileHandler) { this.user = user; this.preferences = preferences; this.openOfferManager = openOfferManager; @@ -73,7 +70,6 @@ class CryptoAccountsDataModel extends ActivatableDataModel { this.accountAgeWitnessService = accountAgeWitnessService; this.persistenceProtoResolver = persistenceProtoResolver; this.corruptedStorageFileHandler = corruptedStorageFileHandler; - this.keyRing = keyRing; setChangeListener = change -> fillAndSortPaymentAccounts(); } @@ -157,12 +153,12 @@ class CryptoAccountsDataModel extends ActivatableDataModel { ArrayList accounts = new ArrayList<>(user.getPaymentAccounts().stream() .filter(paymentAccount -> paymentAccount instanceof AssetAccount) .collect(Collectors.toList())); - GUIUtil.exportAccounts(accounts, accountsFileName, preferences, stage, persistenceProtoResolver, corruptedStorageFileHandler, keyRing); + GUIUtil.exportAccounts(accounts, accountsFileName, preferences, stage, persistenceProtoResolver, corruptedStorageFileHandler); } } public void importAccounts(Stage stage) { - GUIUtil.importAccounts(user, accountsFileName, preferences, stage, persistenceProtoResolver, corruptedStorageFileHandler, keyRing); + GUIUtil.importAccounts(user, accountsFileName, preferences, stage, persistenceProtoResolver, corruptedStorageFileHandler); } public int getNumPaymentAccounts() { diff --git a/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsDataModel.java b/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsDataModel.java index b72b89fe28..909aa945dc 100644 --- a/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsDataModel.java +++ b/desktop/src/main/java/haveno/desktop/main/account/content/traditionalaccounts/TraditionalAccountsDataModel.java @@ -18,7 +18,6 @@ package haveno.desktop.main.account.content.traditionalaccounts; import com.google.inject.Inject; -import haveno.common.crypto.KeyRing; import haveno.common.file.CorruptedStorageFileHandler; import haveno.common.proto.persistable.PersistenceProtoResolver; import haveno.core.account.witness.AccountAgeWitnessService; @@ -56,7 +55,6 @@ class TraditionalAccountsDataModel extends ActivatableDataModel { private final String accountsFileName = "FiatPaymentAccounts"; private final PersistenceProtoResolver persistenceProtoResolver; private final CorruptedStorageFileHandler corruptedStorageFileHandler; - private final KeyRing keyRing; @Inject public TraditionalAccountsDataModel(User user, @@ -65,8 +63,7 @@ class TraditionalAccountsDataModel extends ActivatableDataModel { TradeManager tradeManager, AccountAgeWitnessService accountAgeWitnessService, PersistenceProtoResolver persistenceProtoResolver, - CorruptedStorageFileHandler corruptedStorageFileHandler, - KeyRing keyRing) { + CorruptedStorageFileHandler corruptedStorageFileHandler) { this.user = user; this.preferences = preferences; this.openOfferManager = openOfferManager; @@ -74,7 +71,6 @@ class TraditionalAccountsDataModel extends ActivatableDataModel { this.accountAgeWitnessService = accountAgeWitnessService; this.persistenceProtoResolver = persistenceProtoResolver; this.corruptedStorageFileHandler = corruptedStorageFileHandler; - this.keyRing = keyRing; setChangeListener = change -> fillAndSortPaymentAccounts(); } @@ -159,12 +155,12 @@ class TraditionalAccountsDataModel extends ActivatableDataModel { ArrayList accounts = new ArrayList<>(user.getPaymentAccounts().stream() .filter(paymentAccount -> !(paymentAccount instanceof AssetAccount)) .collect(Collectors.toList())); - GUIUtil.exportAccounts(accounts, accountsFileName, preferences, stage, persistenceProtoResolver, corruptedStorageFileHandler, keyRing); + GUIUtil.exportAccounts(accounts, accountsFileName, preferences, stage, persistenceProtoResolver, corruptedStorageFileHandler); } } public void importAccounts(Stage stage) { - GUIUtil.importAccounts(user, accountsFileName, preferences, stage, persistenceProtoResolver, corruptedStorageFileHandler, keyRing); + GUIUtil.importAccounts(user, accountsFileName, preferences, stage, persistenceProtoResolver, corruptedStorageFileHandler); } public int getNumPaymentAccounts() { diff --git a/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsListItem.java b/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsListItem.java index c9ef1d6cfe..f0baf8c922 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsListItem.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsListItem.java @@ -54,6 +54,7 @@ class TransactionsListItem { private boolean received; private boolean detailsAvailable; private BigInteger amount = BigInteger.ZERO; + private BigInteger txFee = BigInteger.ZERO; private String memo = ""; private long confirmations = 0; @Getter @@ -107,6 +108,7 @@ class TransactionsListItem { amount = valueSentFromMe.multiply(BigInteger.valueOf(-1)); received = false; direction = Res.get("funds.tx.direction.sentTo"); + txFee = tx.getFee().multiply(BigInteger.valueOf(-1)); } if (optionalTradable.isPresent()) { @@ -201,6 +203,14 @@ class TransactionsListItem { return amount; } + public BigInteger getTxFee() { + return txFee; + } + + public String getTxFeeStr() { + return txFee.equals(BigInteger.ZERO) ? "" : HavenoUtils.formatXmr(txFee); + } + public String getAddressString() { return addressString; } diff --git a/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.fxml b/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.fxml index 8cd53a17e3..7c5da97808 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.fxml +++ b/desktop/src/main/java/haveno/desktop/main/funds/transactions/TransactionsView.fxml @@ -36,7 +36,8 @@ - + + 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 f5cca952d6..44fdab6bc0 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 @@ -70,7 +70,7 @@ public class TransactionsView extends ActivatableView { @FXML TableView tableView; @FXML - TableColumn dateColumn, detailsColumn, addressColumn, transactionColumn, amountColumn, memoColumn, confidenceColumn, revertTxColumn; + TableColumn dateColumn, detailsColumn, addressColumn, transactionColumn, amountColumn, txFeeColumn, memoColumn, confidenceColumn, revertTxColumn; @FXML Label numItems; @FXML @@ -89,7 +89,7 @@ public class TransactionsView extends ActivatableView { private EventHandler keyEventEventHandler; private Scene scene; - private TransactionsUpdater transactionsUpdater = new TransactionsUpdater(); + private final TransactionsUpdater transactionsUpdater = new TransactionsUpdater(); private class TransactionsUpdater extends MoneroWalletListener { @Override @@ -129,11 +129,12 @@ public class TransactionsView extends ActivatableView { addressColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.address"))); transactionColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.txId", Res.getBaseCurrencyCode()))); amountColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.amountWithCur", Res.getBaseCurrencyCode()))); + txFeeColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.txFee", Res.getBaseCurrencyCode()))); memoColumn.setGraphic(new AutoTooltipLabel(Res.get("funds.tx.memo"))); confidenceColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.confirmations", Res.getBaseCurrencyCode()))); revertTxColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.revert", Res.getBaseCurrencyCode()))); - tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); + tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_FLEX_LAST_COLUMN); tableView.setPlaceholder(new AutoTooltipLabel(Res.get("funds.tx.noTxAvailable"))); setDateColumnCellFactory(); @@ -141,6 +142,7 @@ public class TransactionsView extends ActivatableView { setAddressColumnCellFactory(); setTransactionColumnCellFactory(); setAmountColumnCellFactory(); + setTxFeeColumnCellFactory(); setMemoColumnCellFactory(); setConfidenceColumnCellFactory(); setRevertTxColumnCellFactory(); @@ -156,7 +158,7 @@ public class TransactionsView extends ActivatableView { addressColumn.setComparator(Comparator.comparing(item -> item.getDirection() + item.getAddressString())); transactionColumn.setComparator(Comparator.comparing(TransactionsListItem::getTxId)); amountColumn.setComparator(Comparator.comparing(TransactionsListItem::getAmount)); - confidenceColumn.setComparator(Comparator.comparingLong(item -> item.getNumConfirmations())); + confidenceColumn.setComparator(Comparator.comparingLong(TransactionsListItem::getNumConfirmations)); memoColumn.setComparator(Comparator.comparing(TransactionsListItem::getMemo)); dateColumn.setSortType(TableColumn.SortType.DESCENDING); @@ -216,8 +218,9 @@ public class TransactionsView extends ActivatableView { columns[2] = item.getDirection() + " " + item.getAddressString(); columns[3] = item.getTxId(); columns[4] = item.getAmountStr(); - columns[5] = item.getMemo() == null ? "" : item.getMemo(); - columns[6] = String.valueOf(item.getNumConfirmations()); + columns[5] = item.getTxFeeStr(); + columns[6] = item.getMemo() == null ? "" : item.getMemo(); + columns[7] = String.valueOf(item.getNumConfirmations()); return columns; }; @@ -414,6 +417,33 @@ public class TransactionsView extends ActivatableView { }); } + + private void setTxFeeColumnCellFactory() { + txFeeColumn.setCellValueFactory((addressListItem) -> + new ReadOnlyObjectWrapper<>(addressListItem.getValue())); + txFeeColumn.setCellFactory( + new Callback<>() { + + @Override + public TableCell call(TableColumn column) { + return new TableCell<>() { + + @Override + public void updateItem(final TransactionsListItem item, boolean empty) { + super.updateItem(item, empty); + + if (item != null && !empty) { + setGraphic(new AutoTooltipLabel(item.getTxFeeStr())); + } else { + setGraphic(null); + } + } + }; + } + }); + } + private void setMemoColumnCellFactory() { memoColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue())); diff --git a/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java b/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java index 7a906ec7d9..997a724224 100644 --- a/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java +++ b/desktop/src/main/java/haveno/desktop/main/overlays/windows/DisputeSummaryWindow.java @@ -678,8 +678,7 @@ public class DisputeSummaryWindow extends Overlay { closeTicketButton.disableProperty().unbind(); hide(); }, (errMessage, err) -> { - log.error("Error closing dispute ticket: " + errMessage); - err.printStackTrace(); + log.error("Error closing dispute ticket: " + errMessage + "\n", err); new Popup().error(err.toString()).show(); }); } diff --git a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/TradeSubView.java b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/TradeSubView.java index eeee387774..cef43e795c 100644 --- a/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/TradeSubView.java +++ b/desktop/src/main/java/haveno/desktop/main/portfolio/pendingtrades/TradeSubView.java @@ -153,8 +153,7 @@ public abstract class TradeSubView extends HBox { tradeStepView.setChatCallback(chatCallback); tradeStepView.activate(); } catch (Exception e) { - log.error("Creating viewClass {} caused an error {}", viewClass, e.getMessage()); - e.printStackTrace(); + log.error("Creating viewClass {} caused an error {}\n", viewClass, e.getMessage(), e); } } diff --git a/desktop/src/main/java/haveno/desktop/main/settings/preferences/PreferencesView.java b/desktop/src/main/java/haveno/desktop/main/settings/preferences/PreferencesView.java index 08e6a083b6..06331e5f1d 100644 --- a/desktop/src/main/java/haveno/desktop/main/settings/preferences/PreferencesView.java +++ b/desktop/src/main/java/haveno/desktop/main/settings/preferences/PreferencesView.java @@ -108,7 +108,7 @@ public class PreferencesView extends ActivatableViewAndModel preferredTradeCurrencyComboBox; private ToggleButton showOwnOffersInOfferBook, useAnimations, useDarkMode, sortMarketCurrenciesNumerically, - avoidStandbyMode, useCustomFee, autoConfirmXmrToggle, hideNonAccountPaymentMethodsToggle, denyApiTakerToggle, + avoidStandbyMode, useSoundForNotifications, useCustomFee, autoConfirmXmrToggle, hideNonAccountPaymentMethodsToggle, denyApiTakerToggle, notifyOnPreReleaseToggle; private int gridRow = 0; private int displayCurrenciesGridRowIndex = 0; @@ -209,7 +209,7 @@ public class PreferencesView extends ActivatableViewAndModel preferences.setUseSoundForNotifications(useSoundForNotifications.isSelected())); } private void activateAutoConfirmPreferences() { diff --git a/desktop/src/main/java/haveno/desktop/main/shared/ChatView.java b/desktop/src/main/java/haveno/desktop/main/shared/ChatView.java index 396d6026a1..236f8471e9 100644 --- a/desktop/src/main/java/haveno/desktop/main/shared/ChatView.java +++ b/desktop/src/main/java/haveno/desktop/main/shared/ChatView.java @@ -65,6 +65,7 @@ import javafx.scene.text.TextAlignment; import javafx.geometry.Insets; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; @@ -565,12 +566,10 @@ public class ChatView extends AnchorPane { inputTextArea.setText(inputTextArea.getText() + "\n[" + Res.get("support.attachment") + " " + result.getName() + "]"); } } catch (java.io.IOException e) { - e.printStackTrace(); - log.error(e.getMessage()); + log.error(ExceptionUtils.getStackTrace(e)); } } catch (MalformedURLException e2) { - e2.printStackTrace(); - log.error(e2.getMessage()); + log.error(ExceptionUtils.getStackTrace(e2)); } } } else { @@ -593,8 +592,7 @@ public class ChatView extends AnchorPane { inputTextArea.setText(inputTextArea.getText() + "\n[" + Res.get("support.attachment") + " " + name + "]"); } } catch (Exception e) { - log.error(e.toString()); - e.printStackTrace(); + log.error(ExceptionUtils.getStackTrace(e)); } } @@ -629,8 +627,7 @@ public class ChatView extends AnchorPane { try (FileOutputStream fileOutputStream = new FileOutputStream(file.getAbsolutePath())) { fileOutputStream.write(attachment.getBytes()); } catch (IOException e) { - e.printStackTrace(); - System.out.println(e.getMessage()); + log.error("Error opening attachment: {}\n", e.getMessage(), e); } } } diff --git a/desktop/src/main/java/haveno/desktop/util/GUIUtil.java b/desktop/src/main/java/haveno/desktop/util/GUIUtil.java index f2ad96c2e0..2c314ea5d8 100644 --- a/desktop/src/main/java/haveno/desktop/util/GUIUtil.java +++ b/desktop/src/main/java/haveno/desktop/util/GUIUtil.java @@ -28,7 +28,6 @@ import com.googlecode.jcsv.writer.internal.CSVWriterBuilder; import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon; import haveno.common.UserThread; import haveno.common.config.Config; -import haveno.common.crypto.KeyRing; import haveno.common.file.CorruptedStorageFileHandler; import haveno.common.persistence.PersistenceManager; import haveno.common.proto.persistable.PersistableEnvelope; @@ -168,12 +167,11 @@ public class GUIUtil { Preferences preferences, Stage stage, PersistenceProtoResolver persistenceProtoResolver, - CorruptedStorageFileHandler corruptedStorageFileHandler, - KeyRing keyRing) { + CorruptedStorageFileHandler corruptedStorageFileHandler) { if (!accounts.isEmpty()) { String directory = getDirectoryFromChooser(preferences, stage); if (!directory.isEmpty()) { - PersistenceManager persistenceManager = new PersistenceManager<>(new File(directory), persistenceProtoResolver, corruptedStorageFileHandler, keyRing); + PersistenceManager persistenceManager = new PersistenceManager<>(new File(directory), persistenceProtoResolver, corruptedStorageFileHandler, null); PaymentAccountList paymentAccounts = new PaymentAccountList(accounts); persistenceManager.initialize(paymentAccounts, fileName, PersistenceManager.Source.PRIVATE_LOW_PRIO); persistenceManager.persistNow(() -> { @@ -193,8 +191,7 @@ public class GUIUtil { Preferences preferences, Stage stage, PersistenceProtoResolver persistenceProtoResolver, - CorruptedStorageFileHandler corruptedStorageFileHandler, - KeyRing keyRing) { + CorruptedStorageFileHandler corruptedStorageFileHandler) { FileChooser fileChooser = new FileChooser(); File initDir = new File(preferences.getDirectoryChooserPath()); if (initDir.isDirectory()) { @@ -207,7 +204,7 @@ public class GUIUtil { if (Paths.get(path).getFileName().toString().equals(fileName)) { String directory = Paths.get(path).getParent().toString(); preferences.setDirectoryChooserPath(directory); - PersistenceManager persistenceManager = new PersistenceManager<>(new File(directory), persistenceProtoResolver, corruptedStorageFileHandler, keyRing); + PersistenceManager persistenceManager = new PersistenceManager<>(new File(directory), persistenceProtoResolver, corruptedStorageFileHandler, null); persistenceManager.readPersisted(fileName, persisted -> { StringBuilder msg = new StringBuilder(); HashSet paymentAccounts = new HashSet<>(); diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index dc626c93e1..5bc11c0134 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -878,9 +878,9 @@ - - - + + + diff --git a/p2p/src/main/java/haveno/network/Socks5ProxyProvider.java b/p2p/src/main/java/haveno/network/Socks5ProxyProvider.java index 8bb3e1d418..f9c498f08e 100644 --- a/p2p/src/main/java/haveno/network/Socks5ProxyProvider.java +++ b/p2p/src/main/java/haveno/network/Socks5ProxyProvider.java @@ -24,6 +24,7 @@ import haveno.common.config.Config; import haveno.network.p2p.network.NetworkNode; import java.net.UnknownHostException; import javax.annotation.Nullable; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -96,8 +97,7 @@ public class Socks5ProxyProvider { try { return new Socks5Proxy(tokens[0], Integer.valueOf(tokens[1])); } catch (UnknownHostException e) { - log.error(e.getMessage()); - e.printStackTrace(); + log.error(ExceptionUtils.getStackTrace(e)); } } else { log.error("Incorrect format for socks5ProxyAddress. Should be: host:port.\n" + diff --git a/p2p/src/main/java/haveno/network/p2p/P2PService.java b/p2p/src/main/java/haveno/network/p2p/P2PService.java index a8028d0e2e..117ed4a494 100644 --- a/p2p/src/main/java/haveno/network/p2p/P2PService.java +++ b/p2p/src/main/java/haveno/network/p2p/P2PService.java @@ -57,6 +57,8 @@ import javafx.beans.property.ReadOnlyIntegerProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleIntegerProperty; import lombok.Getter; + +import org.apache.commons.lang3.exception.ExceptionUtils; import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.Subscription; import org.fxmisc.easybind.monadic.MonadicBinding; @@ -433,15 +435,12 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis @Override public void onFailure(@NotNull Throwable throwable) { - log.error(throwable.toString()); - throwable.printStackTrace(); + log.error(ExceptionUtils.getStackTrace(throwable)); sendDirectMessageListener.onFault(throwable.toString()); } }, MoreExecutors.directExecutor()); } catch (CryptoException e) { - e.printStackTrace(); - log.error(message.toString()); - log.error(e.toString()); + log.error("Error sending encrypted direct message, message={}, error={}\n", message.toString(), e.getMessage(), e); sendDirectMessageListener.onFault(e.toString()); } } diff --git a/p2p/src/main/java/haveno/network/p2p/mailbox/MailboxMessageList.java b/p2p/src/main/java/haveno/network/p2p/mailbox/MailboxMessageList.java index a9d0494908..451d3e7e7f 100644 --- a/p2p/src/main/java/haveno/network/p2p/mailbox/MailboxMessageList.java +++ b/p2p/src/main/java/haveno/network/p2p/mailbox/MailboxMessageList.java @@ -63,8 +63,7 @@ public class MailboxMessageList extends PersistableList { try { return MailboxItem.fromProto(e, networkProtoResolver); } catch (ProtobufferException protobufferException) { - protobufferException.printStackTrace(); - log.error("Error at MailboxItem.fromProto: {}", protobufferException.toString()); + log.error("Error at MailboxItem.fromProto: {}", protobufferException.toString(), protobufferException); return null; } }) diff --git a/p2p/src/main/java/haveno/network/p2p/mailbox/MailboxMessageService.java b/p2p/src/main/java/haveno/network/p2p/mailbox/MailboxMessageService.java index 131b537217..c447b3fccf 100644 --- a/p2p/src/main/java/haveno/network/p2p/mailbox/MailboxMessageService.java +++ b/p2p/src/main/java/haveno/network/p2p/mailbox/MailboxMessageService.java @@ -335,8 +335,7 @@ public class MailboxMessageService implements HashMapChangedListener, PersistedD } }, MoreExecutors.directExecutor()); } catch (CryptoException e) { - log.error("sendEncryptedMessage failed"); - e.printStackTrace(); + log.error("sendEncryptedMessage failed: {}\n", e.getMessage(), e); sendMailboxMessageListener.onFault("sendEncryptedMailboxMessage failed " + e); } } @@ -644,8 +643,7 @@ public class MailboxMessageService implements HashMapChangedListener, PersistedD log.info("The mailboxEntry was already removed earlier."); } } catch (CryptoException e) { - e.printStackTrace(); - log.error("Could not remove ProtectedMailboxStorageEntry from network. Error: {}", e.toString()); + log.error("Could not remove ProtectedMailboxStorageEntry from network. Error: {}\n", e.toString(), e); } } diff --git a/p2p/src/main/java/haveno/network/p2p/network/Connection.java b/p2p/src/main/java/haveno/network/p2p/network/Connection.java index 92eddb0614..1a7f1b84df 100644 --- a/p2p/src/main/java/haveno/network/p2p/network/Connection.java +++ b/p2p/src/main/java/haveno/network/p2p/network/Connection.java @@ -91,6 +91,8 @@ import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import lombok.Getter; import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.lang3.exception.ExceptionUtils; import org.jetbrains.annotations.Nullable; /** @@ -511,8 +513,7 @@ public class Connection implements HasCapabilities, Runnable, MessageListener { Uninterruptibles.sleepUninterruptibly(200, TimeUnit.MILLISECONDS); } catch (Throwable t) { - log.error(t.getMessage()); - t.printStackTrace(); + log.error(ExceptionUtils.getStackTrace(t)); } finally { stopped = true; ThreadUtils.execute(() -> doShutDown(closeConnectionReason, shutDownCompleteHandler), THREAD_ID); @@ -537,16 +538,14 @@ public class Connection implements HasCapabilities, Runnable, MessageListener { } catch (SocketException e) { log.trace("SocketException at shutdown might be expected {}", e.getMessage()); } catch (IOException e) { - log.error("Exception at shutdown. " + e.getMessage()); - e.printStackTrace(); + log.error("Exception at shutdown. {}\n", e.getMessage(), e); } finally { capabilitiesListeners.clear(); try { protoInputStream.close(); } catch (IOException e) { - log.error(e.getMessage()); - e.printStackTrace(); + log.error(ExceptionUtils.getStackTrace(e)); } Utilities.shutdownAndAwaitTermination(executorService, SHUTDOWN_TIMEOUT, TimeUnit.MILLISECONDS); diff --git a/p2p/src/main/java/haveno/network/p2p/network/LocalhostNetworkNode.java b/p2p/src/main/java/haveno/network/p2p/network/LocalhostNetworkNode.java index 9254c9af20..26005988e2 100644 --- a/p2p/src/main/java/haveno/network/p2p/network/LocalhostNetworkNode.java +++ b/p2p/src/main/java/haveno/network/p2p/network/LocalhostNetworkNode.java @@ -76,8 +76,7 @@ public class LocalhostNetworkNode extends NetworkNode { try { startServer(new ServerSocket(servicePort)); } catch (IOException e) { - e.printStackTrace(); - log.error("Exception at startServer: " + e.getMessage()); + log.error("Exception at startServer: {}\n", e.getMessage(), e); } setupListeners.stream().forEach(SetupListener::onHiddenServicePublished); }, simulateTorDelayTorNode, TimeUnit.MILLISECONDS); diff --git a/p2p/src/main/java/haveno/network/p2p/network/Server.java b/p2p/src/main/java/haveno/network/p2p/network/Server.java index 9cf39f570d..437e3d2f88 100644 --- a/p2p/src/main/java/haveno/network/p2p/network/Server.java +++ b/p2p/src/main/java/haveno/network/p2p/network/Server.java @@ -97,11 +97,10 @@ class Server implements Runnable { } } catch (IOException e) { if (isServerActive()) - e.printStackTrace(); + log.error("Error executing server loop: {}\n", e.getMessage(), e); } } catch (Throwable t) { - log.error("Executing task failed. " + t.getMessage()); - t.printStackTrace(); + log.error("Executing task failed: {}\n", t.getMessage(), t); } } diff --git a/p2p/src/main/java/haveno/network/p2p/storage/P2PDataStorage.java b/p2p/src/main/java/haveno/network/p2p/storage/P2PDataStorage.java index a9c0f6ad16..40be21ef32 100644 --- a/p2p/src/main/java/haveno/network/p2p/storage/P2PDataStorage.java +++ b/p2p/src/main/java/haveno/network/p2p/storage/P2PDataStorage.java @@ -974,8 +974,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers broadcaster.broadcast(refreshTTLMessage, sender); } catch (IllegalArgumentException e) { - log.error("refreshTTL failed, missing data: {}", e.toString()); - e.printStackTrace(); + log.error("refreshTTL failed, missing data: {}\n", e.toString(), e); return false; } return true; diff --git a/p2p/src/main/java/haveno/network/p2p/storage/persistence/StoreService.java b/p2p/src/main/java/haveno/network/p2p/storage/persistence/StoreService.java index 6d9f3df16a..17c940add8 100644 --- a/p2p/src/main/java/haveno/network/p2p/storage/persistence/StoreService.java +++ b/p2p/src/main/java/haveno/network/p2p/storage/persistence/StoreService.java @@ -116,8 +116,7 @@ public abstract class StoreService { log.debug("Could not find resourceFile " + resourceFileName + ". That is expected if none is provided yet."); } catch (Throwable e) { log.error("Could not copy resourceFile " + resourceFileName + " to " + - destinationFile.getAbsolutePath() + ".\n" + e.getMessage()); - e.printStackTrace(); + destinationFile.getAbsolutePath() + ".\n", e); } } else { log.debug("No resource file was copied. {} exists already.", fileName); diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto index 03cc6b2305..f63e82a18f 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -1740,6 +1740,7 @@ message PreferencesPayload { string buy_screen_crypto_currency_code = 60; string sell_screen_crypto_currency_code = 61; bool split_offer_output = 62; + bool use_sound_for_notifications = 63; } message AutoConfirmSettings { @@ -1754,6 +1755,7 @@ message XmrNodeSettings { string blockchain_path = 1; string bootstrap_url = 2; repeated string startup_flags = 3; + bool sync_blockchain = 4; } /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/scripts/install_tails/deprecated/README.md b/scripts/install_tails/deprecated/README.md new file mode 100644 index 0000000000..0345bd1ea4 --- /dev/null +++ b/scripts/install_tails/deprecated/README.md @@ -0,0 +1,11 @@ +# Steps to use (This has serious security concerns to tails threat model only run when you need to access haveno) + +## 1. Enable persistent storage and admin password before starting tails + +## 2. Get your haveno deb file in persistent storage (amd64 version for tails) + +## 3. Edit the path to the haveno deb file if necessary then run ```sudo ./haveno-install.sh``` +## 4. As amnesia run ```source ~/.bashrc``` +## 5. Start haveno using ```haveno-tails``` + +## You will need to run this script after each reset, but your data will be saved persistently in /home/amnesia/Persistence/Haveno \ No newline at end of file diff --git a/scripts/install_tails/deprecated/haveno-install.sh b/scripts/install_tails/deprecated/haveno-install.sh new file mode 100644 index 0000000000..247354ff87 --- /dev/null +++ b/scripts/install_tails/deprecated/haveno-install.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +############################################################################# +# Written by BrandyJson, with heavy inspiration from bisq.wiki tails script # +############################################################################# +echo "Installing dpkg from persistent, (1.07-1, if this is out of date change the deb path in the script or manually install after running" +dpkg -i "/home/amnesia/Persistent/haveno_1.0.7-1_amd64.deb" +echo -e "Allowing amnesia to read tor control port cookie, only run this script when you actually want to use haveno\n\n!!! not secure !!!\n" +chmod o+r /var/run/tor/control.authcookie +echo "Updating apparmor-profile" +echo "--- +- apparmor-profiles: + - '/opt/haveno/bin/Haveno' + users: + - 'amnesia' + commands: + AUTHCHALLENGE: + - 'SAFECOOKIE .*' + SETEVENTS: + - 'CIRC ORCONN INFO NOTICE WARN ERR HS_DESC HS_DESC_CONTENT' + GETINFO: + - pattern: 'status/bootstrap-phase' + response: + - pattern: '250-status/bootstrap-phase=*' + replacement: '250-status/bootstrap-phase=NOTICE BOOTSTRAP PROGRESS=100 TAG=done SUMMARY="Done"' + - 'net/listeners/socks' + ADD_ONION: + - pattern: 'NEW:(\S+) Port=9999,(\S+)' + replacement: 'NEW:{} Port=9999,{client-address}:{}' + - pattern: '(\S+):(\S+) Port=9999,(\S+)' + replacement: '{}:{} Port=9999,{client-address}:{}' + DEL_ONION: + - '.+' + HSFETCH: + - '.+' + events: + CIRC: + suppress: true + ORCONN: + suppress: true + INFO: + suppress: true + NOTICE: + suppress: true + WARN: + suppress: true + ERR: + suppress: true + HS_DESC: + response: + - pattern: '650 HS_DESC CREATED (\S+) (\S+) (\S+) \S+ (.+)' + replacement: '650 HS_DESC CREATED {} {} {} redacted {}' + - pattern: '650 HS_DESC UPLOAD (\S+) (\S+) .*' + replacement: '650 HS_DESC UPLOAD {} {} redacted redacted' + - pattern: '650 HS_DESC UPLOADED (\S+) (\S+) .+' + replacement: '650 HS_DESC UPLOADED {} {} redacted' + - pattern: '650 HS_DESC REQUESTED (\S+) NO_AUTH' + replacement: '650 HS_DESC REQUESTED {} NO_AUTH' + - pattern: '650 HS_DESC REQUESTED (\S+) NO_AUTH \S+ \S+' + replacement: '650 HS_DESC REQUESTED {} NO_AUTH redacted redacted' + - pattern: '650 HS_DESC RECEIVED (\S+) NO_AUTH \S+ \S+' + replacement: '650 HS_DESC RECEIVED {} NO_AUTH redacted redacted' + - pattern: '.*' + replacement: '' + HS_DESC_CONTENT: + suppress: true" > /etc/onion-grater.d/haveno.yml +echo "Adding rule to iptables to allow for monero-wallet-rpc to work" +iptables -I OUTPUT 2 -p tcp -d 127.0.0.1 -m tcp --dport 18081 -m owner --uid-owner 1855 -j ACCEPT +echo "Updating torsocks to allow for inbound connection" +sed -i 's/#AllowInbound/AllowInbound/g' /etc/tor/torsocks.conf + +echo "Restarting onion-grater service" + +systemctl restart onion-grater.service + +echo "alias haveno-tails='torsocks /opt/haveno/bin/Haveno --torControlPort 951 --torControlCookieFile=/var/run/tor/control.authcookie --torControlUseSafeCookieAuth --useTorForXmr=ON --userDataDir=/home/amnesia/Persistent/'" >> /home/amnesia/.bashrc +echo -e "Everything is set up just run\n\nsource ~/.bashrc\n\nThen you can start haveno using haveno-tails" diff --git a/seednode/src/main/java/haveno/seednode/SeedNodeMain.java b/seednode/src/main/java/haveno/seednode/SeedNodeMain.java index 5659ab2e0b..455f180934 100644 --- a/seednode/src/main/java/haveno/seednode/SeedNodeMain.java +++ b/seednode/src/main/java/haveno/seednode/SeedNodeMain.java @@ -75,7 +75,7 @@ public class SeedNodeMain extends ExecutableForAppWithP2p { seedNode = new SeedNode(); UserThread.execute(this::onApplicationLaunched); } catch (Exception e) { - e.printStackTrace(); + log.error("Error launching seed node: {}\n", e.toString(), e); } }); }