Merge branch 'master' into appimage-support
This commit is contained in:
commit
984e507ca2
87 changed files with 618 additions and 249 deletions
72
.github/workflows/build.yml
vendored
72
.github/workflows/build.yml
vendored
|
@ -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 fuse
|
||||
|
@ -52,27 +52,77 @@ jobs:
|
|||
./gradlew clean build --refresh-keys --refresh-dependencies
|
||||
./gradlew packageInstallers
|
||||
working-directory: .
|
||||
|
||||
# get version from jar
|
||||
- name: Set Version Unix
|
||||
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
|
||||
- name: Set Version Windows
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
run: |
|
||||
$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/haveno_*.AppImage ${{ github.workspace }}/release
|
||||
if [ "${{ matrix.os }}" == "ubuntu-22.04" ]; then
|
||||
mkdir ${{ github.workspace }}/release-rpm
|
||||
mkdir ${{ github.workspace }}/release-deb
|
||||
mkdir ${{ github.workspace }}/release-appimage
|
||||
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/haveno_*.AppImage ${{ github.workspace }}/release-appimage/Haveno-${{ env.VERSION }}-x86_64.AppImage
|
||||
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
|
||||
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
|
||||
cp desktop/build/temp-*/binaries/desktop-*.jar.SHA-256 ${{ github.workspace }}/release-appimage
|
||||
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
|
||||
|
||||
# win
|
||||
- uses: actions/upload-artifact@v3
|
||||
name: "Windows artifacts"
|
||||
if: ${{ matrix.os == 'windows-latest'}}
|
||||
with:
|
||||
name: HavenoInstaller-${{ matrix.os }}
|
||||
name: haveno-windows
|
||||
path: ${{ github.workspace }}/release
|
||||
# macos
|
||||
- uses: actions/upload-artifact@v3
|
||||
name: "macOS artifacts"
|
||||
if: ${{ matrix.os == 'macos-13' }}
|
||||
with:
|
||||
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: 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: haveno-linux-rpm
|
||||
path: ${{ github.workspace }}/release-rpm
|
||||
- uses: actions/upload-artifact@v3
|
||||
name: "Linux - AppImage artifact"
|
||||
if: ${{ matrix.os == 'ubuntu-22.04' }}
|
||||
with:
|
||||
name: haveno-linux-appimage
|
||||
path: ${{ github.workspace }}/release-appimg
|
|
@ -72,7 +72,7 @@ To bring Haveno to life, we need resources. If you have the possibility, please
|
|||
### Monero
|
||||
|
||||
<p>
|
||||
<img src="https://raw.githubusercontent.com/haveno-dex/haveno/master/media/donate_monero.png" alt="Donate Monero" width="150" height="150"><br>
|
||||
<img src="https://raw.githubusercontent.com/haveno-dex/haveno/master/media/donate_monero.png" alt="Donate Monero" width="115" height="115"><br>
|
||||
<code>42sjokkT9FmiWPqVzrWPFE5NCJXwt96bkBozHf4vgLR9hXyJDqKHEHKVscAARuD7in5wV1meEcSTJTanCTDzidTe2cFXS1F</code>
|
||||
</p>
|
||||
|
||||
|
@ -81,6 +81,6 @@ If you are using a wallet that supports OpenAlias (like the 'official' CLI and G
|
|||
### Bitcoin
|
||||
|
||||
<p>
|
||||
<img src="https://raw.githubusercontent.com/haveno-dex/haveno/master/media/donate_bitcoin.png" alt="Donate Bitcoin" width="150" height="150"><br>
|
||||
<img src="https://raw.githubusercontent.com/haveno-dex/haveno/master/media/donate_bitcoin.png" alt="Donate Bitcoin" width="115" height="115"><br>
|
||||
<code>1AKq3CE1yBAnxGmHXbNFfNYStcByNDc5gQ</code>
|
||||
</p>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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')
|
||||
}
|
||||
|
|
|
@ -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<ILoggingEvent> 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<ILoggingEvent> 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<ILoggingEvent> 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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<T extends Model> {
|
||||
private final Queue<Class<? extends Task<T>>> tasks = new LinkedBlockingQueue<>();
|
||||
|
@ -67,8 +69,8 @@ public class TaskRunner<T extends Model> {
|
|||
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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 +
|
||||
|
|
|
@ -124,7 +124,7 @@ public abstract class HavenoExecutable implements GracefulShutDownHandler, Haven
|
|||
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);
|
||||
}
|
||||
|
@ -201,8 +201,7 @@ public abstract class HavenoExecutable implements GracefulShutDownHandler, Haven
|
|||
startApplication();
|
||||
}
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
log.error("An error occurred: {}", e.getMessage());
|
||||
e.printStackTrace();
|
||||
log.error("An error occurred: {}\n", e.getMessage(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -362,7 +361,7 @@ public abstract class HavenoExecutable implements GracefulShutDownHandler, Haven
|
|||
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();
|
||||
|
@ -397,8 +396,7 @@ public abstract class HavenoExecutable implements GracefulShutDownHandler, Haven
|
|||
});
|
||||
});
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<T extends DisputeList<Dispute>> 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<T extends DisputeList<Dispute>> 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<T extends DisputeList<Dispute>> 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<T extends DisputeList<Dispute>> 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();
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ArbitrationDisputeL
|
|||
requestPersistence(trade);
|
||||
} catch (Exception e) {
|
||||
log.warn("Error processing dispute closed message: {}", e.getMessage());
|
||||
e.printStackTrace();
|
||||
log.warn(ExceptionUtils.getStackTrace(e));
|
||||
requestPersistence(trade);
|
||||
|
||||
// nack bad message and do not reprocess
|
||||
if (e instanceof IllegalArgumentException || e instanceof IllegalStateException) {
|
||||
if (HavenoUtils.isIllegal(e)) {
|
||||
trade.getArbitrator().setDisputeClosedMessage(null); // message is processed
|
||||
trade.setDisputeState(Trade.DisputeState.DISPUTE_CLOSED);
|
||||
String warningMsg = "Error processing dispute closed message: " + e.getMessage() + "\n\nOpen another dispute to try again (ctrl+o).";
|
||||
|
@ -489,8 +491,7 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
|
|||
try {
|
||||
feeEstimateTx = createDisputePayoutTx(trade, dispute.getContract(), disputeResult, false);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.warn("Could not recreate dispute payout tx to verify fee: " + e.getMessage());
|
||||
log.warn("Could not recreate dispute payout tx to verify fee: {}\n", e.getMessage(), e);
|
||||
}
|
||||
if (feeEstimateTx != null) {
|
||||
BigInteger feeEstimate = feeEstimateTx.getFee();
|
||||
|
|
|
@ -27,7 +27,9 @@ import haveno.common.crypto.Hash;
|
|||
import haveno.common.crypto.KeyRing;
|
||||
import haveno.common.crypto.PubKeyRing;
|
||||
import haveno.common.crypto.Sig;
|
||||
import haveno.common.file.FileUtil;
|
||||
import haveno.common.util.Utilities;
|
||||
import haveno.core.api.CoreNotificationService;
|
||||
import haveno.core.api.XmrConnectionService;
|
||||
import haveno.core.app.HavenoSetup;
|
||||
import haveno.core.offer.OfferPayload;
|
||||
|
@ -36,9 +38,12 @@ import haveno.core.support.dispute.arbitration.ArbitrationManager;
|
|||
import haveno.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||
import haveno.core.trade.messages.PaymentReceivedMessage;
|
||||
import haveno.core.trade.messages.PaymentSentMessage;
|
||||
import haveno.core.user.Preferences;
|
||||
import haveno.core.util.JsonUtil;
|
||||
import haveno.core.xmr.wallet.XmrWalletService;
|
||||
import haveno.network.p2p.NodeAddress;
|
||||
|
||||
import java.io.File;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetAddress;
|
||||
|
@ -53,6 +58,13 @@ import java.util.Date;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import javax.sound.sampled.AudioInputStream;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.DataLine;
|
||||
import javax.sound.sampled.SourceDataLine;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import monero.common.MoneroRpcConnection;
|
||||
import monero.daemon.model.MoneroOutput;
|
||||
|
@ -110,11 +122,18 @@ public class HavenoUtils {
|
|||
public static XmrWalletService xmrWalletService;
|
||||
public static XmrConnectionService xmrConnectionService;
|
||||
public static OpenOfferManager openOfferManager;
|
||||
public static CoreNotificationService notificationService;
|
||||
public static Preferences preferences;
|
||||
|
||||
public static boolean isSeedNode() {
|
||||
return havenoSetup == null;
|
||||
}
|
||||
|
||||
public static boolean isDaemon() {
|
||||
if (isSeedNode()) return true;
|
||||
return havenoSetup.getCoreContext().isApiUser();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static Date getReleaseDate() {
|
||||
if (RELEASE_DATE == null) return null;
|
||||
|
@ -510,19 +529,83 @@ public class HavenoUtils {
|
|||
havenoSetup.getTopErrorMsg().set(msg);
|
||||
}
|
||||
|
||||
public static boolean isConnectionRefused(Exception e) {
|
||||
public static boolean isConnectionRefused(Throwable e) {
|
||||
return e != null && e.getMessage().contains("Connection refused");
|
||||
}
|
||||
|
||||
public static boolean isReadTimeout(Exception e) {
|
||||
public static boolean isReadTimeout(Throwable e) {
|
||||
return e != null && e.getMessage().contains("Read timed out");
|
||||
}
|
||||
|
||||
public static boolean isUnresponsive(Exception e) {
|
||||
public static boolean isUnresponsive(Throwable e) {
|
||||
return isConnectionRefused(e) || isReadTimeout(e);
|
||||
}
|
||||
|
||||
public static boolean isNotEnoughSigners(Exception e) {
|
||||
public static boolean isNotEnoughSigners(Throwable e) {
|
||||
return e != null && e.getMessage().contains("Not enough signers");
|
||||
}
|
||||
|
||||
public static boolean isTransactionRejected(Throwable e) {
|
||||
return e != null && e.getMessage().contains("was rejected");
|
||||
}
|
||||
|
||||
public static boolean isIllegal(Throwable e) {
|
||||
return e instanceof IllegalArgumentException || e instanceof IllegalStateException;
|
||||
}
|
||||
|
||||
public static void playChimeSound() {
|
||||
playAudioFile("chime.wav");
|
||||
}
|
||||
|
||||
public static void playCashRegisterSound() {
|
||||
playAudioFile("cash_register.wav");
|
||||
}
|
||||
|
||||
private static void playAudioFile(String fileName) {
|
||||
if (isDaemon()) return; // ignore if running as daemon
|
||||
if (!preferences.getUseSoundForNotificationsProperty().get()) return; // ignore if sounds disabled
|
||||
new Thread(() -> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ public abstract class TradeTask extends Task<Trade> {
|
|||
|
||||
@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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,8 @@ public class XmrNodeSettings implements PersistableEnvelope {
|
|||
String bootstrapUrl;
|
||||
@Nullable
|
||||
List<String> 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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -25,7 +27,7 @@ import monero.wallet.model.MoneroWalletListener;
|
|||
public class XmrWalletBase {
|
||||
|
||||
// constants
|
||||
public static final int SYNC_PROGRESS_TIMEOUT_SECONDS = 60;
|
||||
public static final int SYNC_PROGRESS_TIMEOUT_SECONDS = 120;
|
||||
public static final int DIRECT_SYNC_WITHIN_BLOCKS = 100;
|
||||
|
||||
// inherited
|
||||
|
@ -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;
|
||||
|
|
|
@ -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<XmrAddressEntry> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1503,15 +1500,15 @@ public class XmrWalletService extends XmrWalletBase {
|
|||
}
|
||||
|
||||
// handle success or failure
|
||||
File originalCacheBackup = new File(cachePath + ".backup");
|
||||
if (retrySuccessful) {
|
||||
originalCacheFile.delete(); // delete original wallet cache backup
|
||||
if (originalCacheBackup.exists()) originalCacheBackup.delete(); // delete original wallet cache backup
|
||||
} else {
|
||||
|
||||
// restore original wallet cache
|
||||
log.warn("Failed to open full wallet using backup cache, restoring original cache");
|
||||
File cacheFile = new File(cachePath);
|
||||
if (cacheFile.exists()) cacheFile.delete();
|
||||
File originalCacheBackup = new File(cachePath + ".backup");
|
||||
if (originalCacheBackup.exists()) originalCacheBackup.renameTo(new File(cachePath));
|
||||
|
||||
// throw exception
|
||||
|
@ -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.");
|
||||
}
|
||||
|
@ -1607,15 +1605,15 @@ public class XmrWalletService extends XmrWalletBase {
|
|||
}
|
||||
|
||||
// handle success or failure
|
||||
File originalCacheBackup = new File(cachePath + ".backup");
|
||||
if (retrySuccessful) {
|
||||
originalCacheFile.delete(); // delete original wallet cache backup
|
||||
if (originalCacheBackup.exists()) originalCacheBackup.delete(); // delete original wallet cache backup
|
||||
} else {
|
||||
|
||||
// restore original wallet cache
|
||||
log.warn("Failed to open RPC wallet using backup cache, restoring original cache");
|
||||
File cacheFile = new File(cachePath);
|
||||
if (cacheFile.exists()) cacheFile.delete();
|
||||
File originalCacheBackup = new File(cachePath + ".backup");
|
||||
if (originalCacheBackup.exists()) originalCacheBackup.renameTo(new File(cachePath));
|
||||
|
||||
// throw exception
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
BIN
core/src/main/resources/cash_register.wav
Normal file
BIN
core/src/main/resources/cash_register.wav
Normal file
Binary file not shown.
BIN
core/src/main/resources/chime.wav
Normal file
BIN
core/src/main/resources/chime.wav
Normal file
Binary file not shown.
|
@ -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
|
||||
|
|
|
@ -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í
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=必要承認
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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=已要求确认
|
||||
|
|
|
@ -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=已要求確認
|
||||
|
|
|
@ -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<PaymentAccount> 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() {
|
||||
|
|
|
@ -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<PaymentAccount> 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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,8 @@
|
|||
<TableColumn fx:id="detailsColumn" minWidth="220" maxWidth="220"/>
|
||||
<TableColumn fx:id="addressColumn" minWidth="260"/>
|
||||
<TableColumn fx:id="transactionColumn" minWidth="180"/>
|
||||
<TableColumn fx:id="amountColumn" minWidth="130" maxWidth="130"/>
|
||||
<TableColumn fx:id="amountColumn" minWidth="110" maxWidth="110"/>
|
||||
<TableColumn fx:id="txFeeColumn" minWidth="110" maxWidth="110"/>
|
||||
<TableColumn fx:id="memoColumn" minWidth="40"/>
|
||||
<TableColumn fx:id="confidenceColumn" minWidth="120" maxWidth="130"/>
|
||||
<TableColumn fx:id="revertTxColumn" sortable="false" minWidth="110" maxWidth="110" visible="false"/>
|
||||
|
|
|
@ -70,7 +70,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
|||
@FXML
|
||||
TableView<TransactionsListItem> tableView;
|
||||
@FXML
|
||||
TableColumn<TransactionsListItem, TransactionsListItem> dateColumn, detailsColumn, addressColumn, transactionColumn, amountColumn, memoColumn, confidenceColumn, revertTxColumn;
|
||||
TableColumn<TransactionsListItem, TransactionsListItem> dateColumn, detailsColumn, addressColumn, transactionColumn, amountColumn, txFeeColumn, memoColumn, confidenceColumn, revertTxColumn;
|
||||
@FXML
|
||||
Label numItems;
|
||||
@FXML
|
||||
|
@ -89,7 +89,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
|||
private EventHandler<KeyEvent> 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<VBox, Void> {
|
|||
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<VBox, Void> {
|
|||
setAddressColumnCellFactory();
|
||||
setTransactionColumnCellFactory();
|
||||
setAmountColumnCellFactory();
|
||||
setTxFeeColumnCellFactory();
|
||||
setMemoColumnCellFactory();
|
||||
setConfidenceColumnCellFactory();
|
||||
setRevertTxColumnCellFactory();
|
||||
|
@ -156,7 +158,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
|||
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<VBox, Void> {
|
|||
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<VBox, Void> {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
private void setTxFeeColumnCellFactory() {
|
||||
txFeeColumn.setCellValueFactory((addressListItem) ->
|
||||
new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
txFeeColumn.setCellFactory(
|
||||
new Callback<>() {
|
||||
|
||||
@Override
|
||||
public TableCell<TransactionsListItem, TransactionsListItem> call(TableColumn<TransactionsListItem,
|
||||
TransactionsListItem> 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()));
|
||||
|
|
|
@ -678,7 +678,7 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
closeTicketButton.disableProperty().unbind();
|
||||
hide();
|
||||
}, (errMessage, err) -> {
|
||||
log.error(errMessage);
|
||||
log.error("Error closing dispute ticket: " + errMessage + "\n", err);
|
||||
new Popup().error(err.toString()).show();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
|||
private ComboBox<TradeCurrency> 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<GridPane, Preferenc
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void initializeGeneralOptions() {
|
||||
int titledGroupBgRowSpan = displayStandbyModeFeature ? 7 : 6;
|
||||
int titledGroupBgRowSpan = displayStandbyModeFeature ? 8 : 7;
|
||||
TitledGroupBg titledGroupBg = addTitledGroupBg(root, gridRow, titledGroupBgRowSpan, Res.get("setting.preferences.general"));
|
||||
GridPane.setColumnSpan(titledGroupBg, 1);
|
||||
|
||||
|
@ -285,6 +285,9 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
|||
avoidStandbyMode = addSlideToggleButton(root, ++gridRow,
|
||||
Res.get("setting.preferences.avoidStandbyMode"));
|
||||
}
|
||||
|
||||
useSoundForNotifications = addSlideToggleButton(root, ++gridRow,
|
||||
Res.get("setting.preferences.useSoundForNotifications"), Layout.GROUP_DISTANCE * -1); // TODO: why must negative value be used to place toggle consistently?
|
||||
}
|
||||
|
||||
private void initializeSeparator() {
|
||||
|
@ -518,6 +521,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
|||
GridPane.setHgrow(resetDontShowAgainButton, Priority.ALWAYS);
|
||||
GridPane.setColumnIndex(resetDontShowAgainButton, 0);
|
||||
}
|
||||
|
||||
private void initializeAutoConfirmOptions() {
|
||||
GridPane autoConfirmGridPane = new GridPane();
|
||||
GridPane.setHgrow(autoConfirmGridPane, Priority.ALWAYS);
|
||||
|
@ -790,6 +794,9 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
|||
} else {
|
||||
preferences.setUseStandbyMode(false);
|
||||
}
|
||||
|
||||
useSoundForNotifications.setSelected(preferences.isUseSoundForNotifications());
|
||||
useSoundForNotifications.setOnAction(e -> preferences.setUseSoundForNotifications(useSoundForNotifications.isSelected()));
|
||||
}
|
||||
|
||||
private void activateAutoConfirmPreferences() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<PersistableEnvelope> persistenceManager = new PersistenceManager<>(new File(directory), persistenceProtoResolver, corruptedStorageFileHandler, keyRing);
|
||||
PersistenceManager<PersistableEnvelope> 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<PaymentAccountList> persistenceManager = new PersistenceManager<>(new File(directory), persistenceProtoResolver, corruptedStorageFileHandler, keyRing);
|
||||
PersistenceManager<PaymentAccountList> persistenceManager = new PersistenceManager<>(new File(directory), persistenceProtoResolver, corruptedStorageFileHandler, null);
|
||||
persistenceManager.readPersisted(fileName, persisted -> {
|
||||
StringBuilder msg = new StringBuilder();
|
||||
HashSet<PaymentAccount> paymentAccounts = new HashSet<>();
|
||||
|
|
|
@ -878,9 +878,9 @@
|
|||
<sha256 value="c92e2ca40a3f2474d61e56831aeb379cf8ae3dddeea61b4a828cee2d99f71f38" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.github.woodser" name="monero-java" version="0.8.31">
|
||||
<artifact name="monero-java-0.8.31.jar">
|
||||
<sha256 value="46b81b98bc76f60a965bc7de429ff72cf6c443858987cbd51b9cacd2f8a8d28b" origin="Generated by Gradle"/>
|
||||
<component group="io.github.woodser" name="monero-java" version="0.8.33">
|
||||
<artifact name="monero-java-0.8.33.jar">
|
||||
<sha256 value="f9a02386ec0870b13a512bf5f72da464c9507e1a1ed6982716bff87641f94e81" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.grpc" name="grpc-api" version="1.42.1">
|
||||
|
|
|
@ -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" +
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,8 +63,7 @@ public class MailboxMessageList extends PersistableList<MailboxItem> {
|
|||
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;
|
||||
}
|
||||
})
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -116,8 +116,7 @@ public abstract class StoreService<T extends PersistableEnvelope> {
|
|||
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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Install Haveno on Tails by following these steps:
|
||||
|
||||
1. Enable persistent storage dotfiles and admin password before starting tails.
|
||||
1. Enable persistent storage dotfiles and admin password before starting Tails.
|
||||
2. Download [haveno-install.sh](haveno-install.sh):
|
||||
|
||||
```
|
||||
|
@ -22,3 +22,9 @@ Install Haveno on Tails by following these steps:
|
|||
```
|
||||
|
||||
4. Upon successful execution of the script (no errors), the Haveno release will be installed to persistent storage and can be launched via the desktop shortcut in the 'Other' section of the start menu.
|
||||
|
||||
> [!note]
|
||||
> If you have already installed Haveno on Tails, we recommend moving your data directory (/home/amnesia/Persistent/Haveno-example) to the new default location (/home/amnesia/Persistent/haveno/Data/Haveno-example), to retain your history and for future support.
|
||||
|
||||
> [!note]
|
||||
> Modern versions of Tails will invoke `curl` over Tor, but if your installation does not, then you can add `--socks5-hostname 127.0.0.1:9050` when invoking the install script.
|
11
scripts/install_tails/deprecated/README.md
Normal file
11
scripts/install_tails/deprecated/README.md
Normal file
|
@ -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
|
77
scripts/install_tails/deprecated/haveno-install.sh
Normal file
77
scripts/install_tails/deprecated/haveno-install.sh
Normal file
|
@ -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"
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue