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
- 
+ 
42sjokkT9FmiWPqVzrWPFE5NCJXwt96bkBozHf4vgLR9hXyJDqKHEHKVscAARuD7in5wV1meEcSTJTanCTDzidTe2cFXS1F
@@ -81,6 +81,6 @@ If you are using a wallet that supports OpenAlias (like the 'official' CLI and G
### 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);
}
});
}