Update to 1.0.11

Update to 1.0.11
This commit is contained in:
retoaccess1 2024-09-04 17:50:38 +02:00 committed by GitHub
commit de24c24027
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 233 additions and 72 deletions

View File

@ -71,14 +71,16 @@ To bring Haveno to life, we need resources. If you have the possibility, please
### Monero
`42sjokkT9FmiWPqVzrWPFE5NCJXwt96bkBozHf4vgLR9hXyJDqKHEHKVscAARuD7in5wV1meEcSTJTanCTDzidTe2cFXS1F`
<!-- ![Qr code](https://raw.githubusercontent.com/haveno-dex/haveno/master/media/qrhaveno.png) -->
<p>
<img src="https://raw.githubusercontent.com/haveno-dex/haveno/master/media/donate_monero.png" alt="Donate Monero" width="150" height="150"><br>
<code>42sjokkT9FmiWPqVzrWPFE5NCJXwt96bkBozHf4vgLR9hXyJDqKHEHKVscAARuD7in5wV1meEcSTJTanCTDzidTe2cFXS1F</code>
</p>
If you are using a wallet that supports OpenAlias (like the 'official' CLI and GUI wallets), you can simply put `fund@haveno.exchange` as the "receiver" address.
### Bitcoin
`1AKq3CE1yBAnxGmHXbNFfNYStcByNDc5gQ`
<!-- ![Qr code](https://raw.githubusercontent.com/haveno-dex/haveno/master/media/qrbtc.png) -->
<p>
<img src="https://raw.githubusercontent.com/haveno-dex/haveno/master/media/donate_bitcoin.png" alt="Donate Bitcoin" width="150" height="150"><br>
<code>1AKq3CE1yBAnxGmHXbNFfNYStcByNDc5gQ</code>
</p>

View File

@ -317,6 +317,8 @@ configure(project(':common')) {
exclude(module: 'animal-sniffer-annotations')
}
// override transitive dependency and use latest version from bisq
implementation(group: 'com.github.bisq-network', name: 'jtorctl') { version { strictly "[b2a172f44edcd8deaa5ed75d936dcbb007f0d774]" } }
implementation "org.openjfx:javafx-base:$javafxVersion:$os"
implementation "org.openjfx:javafx-graphics:$javafxVersion:$os"
}
@ -607,7 +609,7 @@ configure(project(':desktop')) {
apply plugin: 'com.github.johnrengelman.shadow'
apply from: 'package/package.gradle'
version = '1.0.10-SNAPSHOT'
version = '1.0.11-SNAPSHOT'
jar.manifest.attributes(
"Implementation-Title": project.name,

View File

@ -28,7 +28,7 @@ import static com.google.common.base.Preconditions.checkArgument;
public class Version {
// The application versions
// We use semantic versioning with major, minor and patch
public static final String VERSION = "1.0.10";
public static final String VERSION = "1.0.11";
/**
* Holds a list of the tagged resource files for optimizing the getData requests.

View File

@ -40,10 +40,13 @@ import java.util.Scanner;
@Slf4j
public class FileUtil {
private static final String BACKUP_DIR = "backup";
public static void rollingBackup(File dir, String fileName, int numMaxBackupFiles) {
if (numMaxBackupFiles <= 0) return;
if (dir.exists()) {
File backupDir = new File(Paths.get(dir.getAbsolutePath(), "backup").toString());
File backupDir = new File(Paths.get(dir.getAbsolutePath(), BACKUP_DIR).toString());
if (!backupDir.exists())
if (!backupDir.mkdir())
log.warn("make dir failed.\nBackupDir=" + backupDir.getAbsolutePath());
@ -72,8 +75,21 @@ public class FileUtil {
}
}
public static File getLatestBackupFile(File dir, String fileName) {
File backupDir = new File(Paths.get(dir.getAbsolutePath(), BACKUP_DIR).toString());
if (!backupDir.exists()) return null;
String dirName = "backups_" + fileName;
if (dirName.contains(".")) dirName = dirName.replace(".", "_");
File backupFileDir = new File(Paths.get(backupDir.getAbsolutePath(), dirName).toString());
if (!backupFileDir.exists()) return null;
File[] files = backupFileDir.listFiles();
if (files == null || files.length == 0) return null;
Arrays.sort(files, Comparator.comparing(File::getName));
return files[files.length - 1];
}
public static void deleteRollingBackup(File dir, String fileName) {
File backupDir = new File(Paths.get(dir.getAbsolutePath(), "backup").toString());
File backupDir = new File(Paths.get(dir.getAbsolutePath(), BACKUP_DIR).toString());
if (!backupDir.exists()) return;
String dirName = "backups_" + fileName;
if (dirName.contains(".")) dirName = dirName.replace(".", "_");

View File

@ -99,9 +99,10 @@ public final class XmrConnectionService {
@Getter
private MoneroDaemonInfo lastInfo;
private Long lastLogPollErrorTimestamp;
private Long syncStartHeight = null;
private long lastLogDaemonNotSyncedTimestamp;
private Long syncStartHeight;
private TaskLooper daemonPollLooper;
private long lastRefreshPeriodMs = 0;
private long lastRefreshPeriodMs;
@Getter
private boolean isShutDownStarted;
private List<MoneroConnectionManagerListener> listeners = new ArrayList<>();
@ -371,7 +372,6 @@ public final class XmrConnectionService {
Long targetHeight = getTargetHeight();
if (targetHeight == null) return false;
if (targetHeight - chainHeight.get() <= 3) return true; // synced if within 3 blocks of target height
log.warn("Our chain height: {} is out of sync with peer nodes chain height: {}", chainHeight.get(), targetHeight);
return false;
}
@ -720,7 +720,7 @@ public final class XmrConnectionService {
}
// log error message periodically
if ((lastLogPollErrorTimestamp == null || System.currentTimeMillis() - lastLogPollErrorTimestamp > HavenoUtils.LOG_POLL_ERROR_PERIOD_MS)) {
if (lastLogPollErrorTimestamp == null || System.currentTimeMillis() - lastLogPollErrorTimestamp > HavenoUtils.LOG_POLL_ERROR_PERIOD_MS) {
log.warn("Failed to fetch daemon info, trying to switch to best connection: " + e.getMessage());
if (DevEnv.isDevMode()) e.printStackTrace();
lastLogPollErrorTimestamp = System.currentTimeMillis();
@ -734,6 +734,12 @@ public final class XmrConnectionService {
// connected to daemon
isConnected = true;
// throttle warnings if daemon not synced
if (!isSyncedWithinTolerance() && System.currentTimeMillis() - lastLogDaemonNotSyncedTimestamp > HavenoUtils.LOG_DAEMON_NOT_SYNCED_WARN_PERIOD_MS) {
log.warn("Our chain height: {} is out of sync with peer nodes chain height: {}", chainHeight.get(), getTargetHeight());
lastLogDaemonNotSyncedTimestamp = System.currentTimeMillis();
}
// announce connection change if refresh period changes
if (getRefreshPeriodMs() != lastRefreshPeriodMs) {
lastRefreshPeriodMs = getRefreshPeriodMs();

View File

@ -87,7 +87,7 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
//if (true) throw new RuntimeException("Pretend error");
reserveTx = model.getXmrWalletService().createReserveTx(penaltyFee, makerFee, sendAmount, securityDeposit, returnAddress, openOffer.isReserveExactAmount(), preferredSubaddressIndex);
} catch (Exception e) {
log.warn("Error creating reserve tx, offerId={}, attempt={}/{}, error={}", i + 1, TradeProtocol.MAX_ATTEMPTS, openOffer.getShortId(), e.getMessage());
log.warn("Error creating reserve tx, offerId={}, attempt={}/{}, error={}", openOffer.getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage());
model.getXmrWalletService().handleWalletError(e, sourceConnection);
verifyPending();
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;

View File

@ -511,6 +511,7 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
break;
} catch (Exception e) {
if (trade.isPayoutPublished()) throw new IllegalStateException("Payout tx already published for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
if (HavenoUtils.isNotEnoughSigners(e)) throw new IllegalArgumentException(e);
log.warn("Failed to submit dispute payout tx, tradeId={}, attempt={}/{}, error={}", trade.getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage());
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;
if (trade.getXmrConnectionService().isConnected()) trade.requestSwitchToNextBestConnection(sourceConnection);

View File

@ -81,6 +81,7 @@ public class HavenoUtils {
// other configuration
public static final long LOG_POLL_ERROR_PERIOD_MS = 1000 * 60 * 4; // log poll errors up to once every 4 minutes
public static final long LOG_DAEMON_NOT_SYNCED_WARN_PERIOD_MS = 1000 * 30; // log warnings when daemon not synced once every 30s
// synchronize requests to the daemon
private static boolean SYNC_DAEMON_REQUESTS = false; // sync long requests to daemon (e.g. refresh, update pool) // TODO: performance suffers by syncing daemon requests, but otherwise we sometimes get sporadic errors?
@ -520,4 +521,8 @@ public class HavenoUtils {
public static boolean isUnresponsive(Exception e) {
return isConnectionRefused(e) || isReadTimeout(e);
}
public static boolean isNotEnoughSigners(Exception e) {
return e != null && e.getMessage().contains("Not enough signers");
}
}

View File

@ -1190,7 +1190,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
} catch (IllegalArgumentException | IllegalStateException e) {
throw e;
} catch (Exception e) {
log.warn("Failed to create payout tx, tradeId={}, attempt={}/{}, error={}", i + 1, TradeProtocol.MAX_ATTEMPTS, getShortId(), e.getMessage());
log.warn("Failed to create payout tx, tradeId={}, attempt={}/{}, error={}", getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage());
handleWalletError(e, sourceConnection);
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
@ -1250,7 +1250,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
throw e;
} catch (Exception e) {
if (e.getMessage().contains("not possible")) throw new IllegalArgumentException("Loser payout is too small to cover the mining fee");
log.warn("Failed to create dispute payout tx, tradeId={}, attempt={}/{}, error={}", i + 1, TradeProtocol.MAX_ATTEMPTS, getShortId(), e.getMessage());
log.warn("Failed to create dispute payout tx, tradeId={}, attempt={}/{}, error={}", getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage());
handleWalletError(e, sourceConnection);
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
@ -1279,7 +1279,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
} catch (IllegalArgumentException | IllegalStateException e) {
throw e;
} catch (Exception e) {
log.warn("Failed to process payout tx, attempt={}/{}, tradeId={}, error={}", i + 1, TradeProtocol.MAX_ATTEMPTS, getShortId(), e.getMessage());
log.warn("Failed to process payout tx, tradeId={}, attempt={}/{}, error={}", getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage());
handleWalletError(e, sourceConnection);
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
@ -1381,6 +1381,8 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
wallet.submitMultisigTxHex(payoutTxHex);
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);
}
}

View File

@ -104,7 +104,7 @@ public class MaybeSendSignContractRequest extends TradeTask {
try {
depositTx = trade.getXmrWalletService().createDepositTx(trade, reserveExactAmount, subaddressIndex);
} catch (Exception e) {
log.warn("Error creating deposit tx, attempt={}/{}, tradeId={}, error={}", i + 1, TradeProtocol.MAX_ATTEMPTS, trade.getShortId(), e.getMessage());
log.warn("Error creating deposit tx, tradeId={}, attempt={}/{}, error={}", trade.getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage());
trade.getXmrWalletService().handleWalletError(e, sourceConnection);
if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while creating deposit tx, tradeId=" + trade.getShortId());
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;

View File

@ -70,7 +70,7 @@ public class TakerReserveTradeFunds extends TradeTask {
try {
reserveTx = model.getXmrWalletService().createReserveTx(penaltyFee, takerFee, sendAmount, securityDeposit, returnAddress, false, null);
} catch (Exception e) {
log.warn("Error creating reserve tx, attempt={}/{}, tradeId={}, error={}", i + 1, TradeProtocol.MAX_ATTEMPTS, trade.getShortId(), e.getMessage());
log.warn("Error creating reserve tx, tradeId={}, attempt={}/{}, error={}", trade.getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage());
trade.getXmrWalletService().handleWalletError(e, sourceConnection);
if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while creating reserve tx, tradeId=" + trade.getShortId());
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;

View File

@ -136,7 +136,6 @@ public class XmrWalletService extends XmrWalletBase {
private final WalletsSetup walletsSetup;
private final File walletDir;
private final File xmrWalletFile;
private final int rpcBindPort;
private final boolean useNativeXmrWallet;
protected final CopyOnWriteArraySet<XmrBalanceListener> balanceListeners = new CopyOnWriteArraySet<>();
@ -151,6 +150,7 @@ public class XmrWalletService extends XmrWalletBase {
private TaskLooper pollLooper;
private boolean pollInProgress;
private Long pollPeriodMs;
private long lastLogDaemonNotSyncedTimestamp;
private long lastLogPollErrorTimestamp;
private long lastPollTxsTimestamp;
private final Object pollLock = new Object();
@ -180,7 +180,6 @@ public class XmrWalletService extends XmrWalletBase {
this.walletDir = walletDir;
this.rpcBindPort = rpcBindPort;
this.useNativeXmrWallet = useNativeXmrWallet;
this.xmrWalletFile = new File(walletDir, MONERO_WALLET_NAME);
HavenoUtils.xmrWalletService = this;
HavenoUtils.xmrConnectionService = xmrConnectionService;
this.xmrConnectionService = xmrConnectionService; // TODO: super's is null unless set here from injection
@ -1326,7 +1325,7 @@ public class XmrWalletService extends XmrWalletBase {
if (wallet == null) {
MoneroDaemonRpc daemon = xmrConnectionService.getDaemon();
log.info("Initializing main wallet with monerod=" + (daemon == null ? "null" : daemon.getRpcConnection().getUri()));
if (MoneroUtils.walletExists(xmrWalletFile.getPath())) {
if (walletExists(MONERO_WALLET_NAME)) {
wallet = openWallet(MONERO_WALLET_NAME, rpcBindPort, isProxyApplied(wasWalletSynced));
} else if (Boolean.TRUE.equals(xmrConnectionService.isConnected())) {
wallet = createWallet(MONERO_WALLET_NAME, rpcBindPort);
@ -1474,11 +1473,54 @@ public class XmrWalletService extends XmrWalletBase {
MoneroRpcConnection connection = new MoneroRpcConnection(xmrConnectionService.getConnection());
if (!applyProxyUri) connection.setProxyUri(null);
// open wallet
// try opening wallet
config.setNetworkType(getMoneroNetworkType());
config.setServer(connection);
log.info("Opening full wallet " + config.getPath() + " with monerod=" + connection.getUri() + ", proxyUri=" + connection.getProxyUri());
walletFull = MoneroWalletFull.openWallet(config);
try {
walletFull = MoneroWalletFull.openWallet(config);
} catch (Exception e) {
log.warn("Failed to open full wallet '{}', attempting to use backup cache, error={}", config.getPath(), e.getMessage());
boolean retrySuccessful = false;
try {
// rename wallet cache to backup
String cachePath = walletDir.toString() + File.separator + MONERO_WALLET_NAME;
File originalCacheFile = new File(cachePath);
if (originalCacheFile.exists()) originalCacheFile.renameTo(new File(cachePath + ".backup"));
// copy latest wallet cache backup to main folder
File backupCacheFile = FileUtil.getLatestBackupFile(walletDir, MONERO_WALLET_NAME);
if (backupCacheFile != null) FileUtil.copyFile(backupCacheFile, new File(cachePath));
// retry opening wallet without original cache
try {
walletFull = MoneroWalletFull.openWallet(config);
log.info("Successfully opened full wallet using backup cache");
retrySuccessful = true;
} catch (Exception e2) {
// ignore
}
// handle success or failure
if (retrySuccessful) {
originalCacheFile.delete(); // delete original wallet cache backup
} else {
// restore original wallet cache
log.warn("Failed to open full wallet using backup cache, restoring original cache");
File cacheFile = new File(cachePath);
if (cacheFile.exists()) cacheFile.delete();
File originalCacheBackup = new File(cachePath + ".backup");
if (originalCacheBackup.exists()) originalCacheBackup.renameTo(new File(cachePath));
// throw exception
throw e;
}
} catch (Exception e2) {
throw e; // throw original exception
}
}
if (walletFull.getDaemonConnection() != null) walletFull.getDaemonConnection().setPrintStackTrace(PRINT_RPC_STACK_TRACE);
log.info("Done opening full wallet " + config.getPath());
return walletFull;
@ -1517,7 +1559,7 @@ public class XmrWalletService extends XmrWalletBase {
} catch (Exception e) {
e.printStackTrace();
if (walletRpc != null) forceCloseWallet(walletRpc, config.getPath());
throw new IllegalStateException("Could not create wallet '" + config.getPath() + "'. Please close Haveno, stop all monero-wallet-rpc processes, and restart Haveno.");
throw new IllegalStateException("Could not create wallet '" + config.getPath() + "'. Please close Haveno, stop all monero-wallet-rpc processes in your task manager, and restart Haveno.");
}
}
@ -1536,17 +1578,60 @@ public class XmrWalletService extends XmrWalletBase {
MoneroRpcConnection connection = new MoneroRpcConnection(xmrConnectionService.getConnection());
if (!applyProxyUri) connection.setProxyUri(null);
// open wallet
// try opening wallet
log.info("Opening RPC wallet " + config.getPath() + " with monerod=" + connection.getUri() + ", proxyUri=" + connection.getProxyUri());
config.setServer(connection);
walletRpc.openWallet(config);
try {
walletRpc.openWallet(config);
} catch (Exception e) {
log.warn("Failed to open RPC wallet '{}', attempting to use backup cache, error={}", config.getPath(), e.getMessage());
boolean retrySuccessful = false;
try {
// rename wallet cache to backup
String cachePath = walletDir.toString() + File.separator + MONERO_WALLET_NAME;
File originalCacheFile = new File(cachePath);
if (originalCacheFile.exists()) originalCacheFile.renameTo(new File(cachePath + ".backup"));
// copy latest wallet cache backup to main folder
File backupCacheFile = FileUtil.getLatestBackupFile(walletDir, MONERO_WALLET_NAME);
if (backupCacheFile != null) FileUtil.copyFile(backupCacheFile, new File(cachePath));
// retry opening wallet without original cache
try {
walletRpc.openWallet(config);
log.info("Successfully opened RPC wallet using backup cache");
retrySuccessful = true;
} catch (Exception e2) {
// ignore
}
// handle success or failure
if (retrySuccessful) {
originalCacheFile.delete(); // delete original wallet cache backup
} else {
// restore original wallet cache
log.warn("Failed to open RPC wallet using backup cache, restoring original cache");
File cacheFile = new File(cachePath);
if (cacheFile.exists()) cacheFile.delete();
File originalCacheBackup = new File(cachePath + ".backup");
if (originalCacheBackup.exists()) originalCacheBackup.renameTo(new File(cachePath));
// throw exception
throw e;
}
} catch (Exception e2) {
throw e; // throw original exception
}
}
if (walletRpc.getDaemonConnection() != null) walletRpc.getDaemonConnection().setPrintStackTrace(PRINT_RPC_STACK_TRACE);
log.info("Done opening RPC wallet " + config.getPath());
return walletRpc;
} catch (Exception e) {
e.printStackTrace();
if (walletRpc != null) forceCloseWallet(walletRpc, config.getPath());
throw new IllegalStateException("Could not open wallet '" + config.getPath() + "'. Please close Haveno, stop all monero-wallet-rpc processes, and restart Haveno.\n\nError message: " + e.getMessage());
throw new IllegalStateException("Could not open wallet '" + config.getPath() + "'. Please close Haveno, stop all monero-wallet-rpc processes in your task manager, and restart Haveno.\n\nError message: " + e.getMessage());
}
}
@ -1767,9 +1852,15 @@ public class XmrWalletService extends XmrWalletBase {
return;
}
if (!xmrConnectionService.isSyncedWithinTolerance()) {
log.warn("Monero daemon is not synced within tolerance, height={}, targetHeight={}", xmrConnectionService.chainHeightProperty().get(), xmrConnectionService.getTargetHeight());
// throttle warnings
if (System.currentTimeMillis() - lastLogDaemonNotSyncedTimestamp > HavenoUtils.LOG_DAEMON_NOT_SYNCED_WARN_PERIOD_MS) {
log.warn("Monero daemon is not synced within tolerance, height={}, targetHeight={}, monerod={}", xmrConnectionService.chainHeightProperty().get(), xmrConnectionService.getTargetHeight(), xmrConnectionService.getConnection() == null ? null : xmrConnectionService.getConnection().getUri());
lastLogDaemonNotSyncedTimestamp = System.currentTimeMillis();
}
return;
}
// sync wallet if behind daemon
if (walletHeight.get() < xmrConnectionService.getTargetHeight()) {
synchronized (walletLock) { // avoid long sync from blocking other operations

View File

@ -5,10 +5,10 @@
<!-- See: https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -->
<key>CFBundleVersion</key>
<string>1.0.10</string>
<string>1.0.11</string>
<key>CFBundleShortVersionString</key>
<string>1.0.10</string>
<string>1.0.11</string>
<key>CFBundleExecutable</key>
<string>Haveno</string>

View File

@ -242,8 +242,12 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
if (GUIUtil.isReadyForTxBroadcastOrShowPopup(xmrWalletService)) {
try {
// collect tx fields to local variables
String withdrawToAddress = withdrawToTextField.getText();
boolean sendMax = this.sendMax;
BigInteger amount = this.amount;
// validate address
final String withdrawToAddress = withdrawToTextField.getText();
if (!MoneroUtils.isValidAddress(withdrawToAddress, XmrWalletService.getMoneroNetworkType())) {
throw new IllegalArgumentException(Res.get("validation.xmr.invalidAddress"));
}
@ -298,7 +302,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
BigInteger receiverAmount = tx.getOutgoingTransfer().getDestinations().get(0).getAmount();
BigInteger fee = tx.getFee();
String messageText = Res.get("shared.sendFundsDetailsWithFee",
HavenoUtils.formatXmr(amount, true),
HavenoUtils.formatXmr(receiverAmount.add(fee), true),
withdrawToAddress,
HavenoUtils.formatXmr(fee, true),
HavenoUtils.formatXmr(receiverAmount, true));

View File

@ -57,6 +57,12 @@ For example, change "Haveno" to "HavenoX", which will use this application folde
- macOS: ~/Library/Application Support/HavenoX/
- Windows: ~\AppData\Roaming\HavenoX\
## Change the P2P network version
To avoid interference with other networks, change `P2P_NETWORK_VERSION` in [Version.java](https://github.com/haveno-dex/haveno/blob/a7e90395d24ec3d33262dd5d09c5faec61651a51/common/src/main/java/haveno/common/app/Version.java#L83).
For example, change it to `"B"`.
## Start the seed nodes
Rebuild for the previous changes to the source code to take effect: `make skip-tests`.

View File

@ -219,6 +219,12 @@ For example, change "Haveno" to "HavenoX", which will use this application folde
- macOS: ~/Library/Application Support/HavenoX/
- Windows: ~\AppData\Roaming\HavenoX\
## Change the P2P network version
To avoid interference with other networks, change `P2P_NETWORK_VERSION` in [Version.java](https://github.com/haveno-dex/haveno/blob/a7e90395d24ec3d33262dd5d09c5faec61651a51/common/src/main/java/haveno/common/app/Version.java#L83).
For example, change it to `"B"`.
## Set the network's release date
Optionally set the network's approximate release date by setting `RELEASE_DATE` in HavenoUtils.java.

View File

@ -1,18 +1,19 @@
# Set up environment
# Build and run Haveno
These are the steps needed to build Haveno and test it on our test network or locally.
These are the steps needed to build and run Haveno. You can test it locally or on our test network using the official Haveno repository.
> [!note]
> Trying to use Haveno on mainnet?
>
> The official Haveno repository does not operate or endorse any mainnet network.
>
> Find a third party network and use their installer or build their repository. Alternatively [create your own mainnet network](create-mainnet.md).
## Install dependencies
On Linux and macOS, install Java JDK 21:
```
curl -s "https://get.sdkman.io" | bash
sdk install java 21.0.2.fx-librca
```
On Windows, install MSYS2 and Java JDK 21:
On Ubuntu: `sudo apt install make wget git`
On Windows, first install MSYS2:
1. Install [MSYS2](https://www.msys2.org/).
2. Start MSYS2 MINGW64 or MSYS MINGW32 depending on your system. Use MSYS2 for all commands throughout this document.
4. Update pacman: `pacman -Syy`
@ -21,12 +22,17 @@ On Windows, install MSYS2 and Java JDK 21:
64-bit: `pacman -S mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake git`
32-bit: `pacman -S mingw-w64-i686-toolchain make mingw-w64-i686-cmake git`
6. `curl -s "https://get.sdkman.io" | bash`
7. `sdk install java 21.0.2.fx-librca`
On all platforms, install Java JDK 21:
```
curl -s "https://get.sdkman.io" | bash
sdk install java 21.0.2.fx-librca
```
## Build Haveno
If it's the first time you are building Haveno, run the following commands to download the repository, the needed dependencies, and build the latest release:
If it's the first time you are building Haveno, run the following commands to download the repository, the needed dependencies, and build the latest release. If using a third party network, replace the repository URL with theirs:
```
git clone https://github.com/haveno-dex/haveno.git
@ -45,15 +51,23 @@ git pull
make clean && make
```
Make sure to delete the folder with the local settings, as there are breaking changes between releases:
## Run Haveno
On **Linux**: remove everything inside `~/.local/share/haveno-*/xmr_stagenet/`, except the `wallet` folder.
> [!note]
> When you run Haveno, your application folder will be installed to:
> * Linux: `~/.local/share/Haveno/`
> * macOS: `~/Library/Application\ Support/Haveno/`
> * Windows: `~\AppData\Roaming\Haveno\`
On **Mac**: remove everything inside `~/Library/Application\ Support/haveno-*/xmr_stagenet/`, except the `wallet` folder.
### Mainnet
On **Windows**: remove everything inside `~\AppData\Roaming\haveno-*/xmr_stagenet/`, except the `wallet` folder.
If you are building a third party repository which supports mainnet, you can start Haveno with:
## Join the public test network
```
make haveno-desktop-mainnet
```
### Join the public test network
If you want to try Haveno in a live setup, launch a Haveno instance that will connect to other peers on our public test environment, which runs on Monero's stagenet (you won't need to download the blockchain locally). You'll be able to make test trades with other users and have a preview of Haveno's trade protocol in action. Note that development is very much ongoing. Things are slow and might break.
@ -67,11 +81,11 @@ Steps:
6. Now if you are taking a trade you'll be asked to confirm you have sent the payment outside Haveno. Confirm in the app and wait for the confirmation of received payment from the other trader.
7. Once the other trader confirms, deposits are sent back to the owners and the trade is complete.
# Run a local test network
### Run a local test network
If you are a developer who wants to test Haveno in a more controlled way, follow the next steps to build a local test environment.
## Run a local XMR testnet
#### Run a local XMR testnet
1. In a new terminal window run `make monerod1-local`
1. In a new terminal window run `make monerod2-local`
@ -79,7 +93,7 @@ If you are a developer who wants to test Haveno in a more controlled way, follow
`start_mining 9tsUiG9bwcU7oTbAdBwBk2PzxFtysge5qcEsHEpetmEKgerHQa1fDqH7a4FiquZmms7yM22jdifVAD7jAb2e63GSJMuhY75 1`
## Deploy
#### Deploy
If you are a *screen* user, simply run `make deploy`. This command will open all needed Haveno instances (seednode, user1, user2, arbitrator) using *screen*.
@ -93,7 +107,7 @@ If you don't use *screen*, open 4 terminal windows and run in each one of them:
If this is the first time launching the arbitrator desktop application, register the arbitrator after the interface opens. Go to the *Account* tab and press `cmd+r`. Confirm the registration of the arbitrator.
## Fund your wallets
#### Fund your wallets
When running user1 and user2, you'll see a Monero address prompted in the terminal. Send test XMR to the addresses of both user1 and user2 to be able to initiate a trade.
@ -101,6 +115,6 @@ You can fund the two wallets by mining some test XMR coins to those addresses. T
monerod will start mining local testnet coins on your device using one thread. Replace `ADDRESS` with the address of user1 first, and then user2's. Run `stop_mining` to stop mining.
## Start testing
#### Start testing
You are all set. Now that everything is running and your wallets are funded, you can create test trades between user1 and user2. Remember to mine a few blocks after opening and accepting the test trade so the transaction will be confirmed.

View File

@ -2,10 +2,24 @@
This document is a guide for Haveno users.
# Running a Local Monero Node
## Running a Local Monero Node
For the best experience using Haveno, it is highly recommended to run your own local Monero node to improve security and responsiveness.
By default, Haveno will automatically connect to a local node if it is detected. Additionally, Haveno will automatically start and connect to your local Monero node if it was last used and is currently offline.
Otherwise, Haveno will connect to a pre-configured remote node, unless manually configured otherwise.
Otherwise, Haveno will connect to a pre-configured remote node, unless manually configured otherwise.
## UI Scaling For High DPI Displays
If the UI is too small on your display, you can force UI scaling to a value of your choice using one of the following approaches. The examples below scale the UI to 200%, you can replace the '2' with a value of your choice, e.g. '1.5' for 150%.
### Edit The Application Shortcut (KDE Plasma)
1) Open the properties of your shortcut to haveno
2) Click on Program
3) Add `JAVA_TOOL_OPTIONS=-Dglass.gtk.uiScale=2` to the environment variables
### Launching From The Command Line
Prepend `JAVA_TOOL_OPTIONS=-Dglass.gtk.uiScale=2` to the command you use to launch haveno (e.g. `JAVA_TOOL_OPTIONS=-Dglass.gtk.uiScale=2 haveno-desktop`).

View File

@ -173,14 +173,6 @@
<sha256 value="b7774955491262076d4fec12de392628a541e0560018142a0a6e38f72bb96010" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.github.JesusMcCloud" name="jtorctl" version="9b5ba2036b">
<artifact name="jtorctl-9b5ba2036b.jar">
<sha256 value="b2bdfe9758e4c82ff1b10e7c3098981bf55ea3e5f161ee7990ac125003a6cdbe" origin="Generated by Gradle"/>
</artifact>
<artifact name="jtorctl-9b5ba2036b.pom">
<sha256 value="218fbc37a57bcf888af917220c31acea39197859cc47519c12f1151fa4a27812" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.github.bisq-network" name="bitcoinj" version="2a80db4">
<artifact name="bitcoinj-2a80db4.jar">
<sha256 value="65ed08fa5777ea4a08599bdd575e7dc1f4ba2d4d5835472551439d6f6252e68a" origin="Generated by Gradle"/>

BIN
media/donate_bitcoin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
media/donate_monero.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -41,7 +41,7 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SeedNodeMain extends ExecutableForAppWithP2p {
private static final long CHECK_CONNECTION_LOSS_SEC = 30;
private static final String VERSION = "1.0.10";
private static final String VERSION = "1.0.11";
private SeedNode seedNode;
private Timer checkConnectionLossTime;

View File

@ -124,8 +124,8 @@ HiddenServiceEnableIntroDoSDefense 1
## Proof of Work (PoW) before establishing Rendezvous Circuits
## The lower the queue and burst rates, the higher the puzzle effort tends to be for users.
HiddenServicePoWDefensesEnabled 1
HiddenServicePoWQueueRate 200 # (Default: 250)
HiddenServicePoWQueueBurst 1000 # (Default: 2500)
HiddenServicePoWQueueRate 50 # (Default: 250)
HiddenServicePoWQueueBurst 250 # (Default: 2500)
## Stream limits in the established Rendezvous Circuits
## The maximum number of simultaneous streams (connections) per rendezvous circuit. The max value allowed is 65535. (0 = unlimited)
@ -143,8 +143,8 @@ HiddenServiceEnableIntroDoSDefense 1
#HiddenServiceNumIntroductionPoints 3 # (Default: 3)
HiddenServicePoWDefensesEnabled 1
HiddenServicePoWQueueRate 200 # (Default: 250)
HiddenServicePoWQueueBurst 1000 # (Default: 2500)
HiddenServicePoWQueueRate 50 # (Default: 250)
HiddenServicePoWQueueBurst 250 # (Default: 2500)
HiddenServiceMaxStreams 25
#HiddenServiceMaxStreamsCloseCircuit 1