mirror of
https://github.com/boldsuck/haveno.git
synced 2024-12-22 20:19:21 +00:00
refactor syncing wallet with progress and switching connections
This commit is contained in:
parent
dbb3d4f891
commit
1b5c03bce8
12 changed files with 240 additions and 141 deletions
|
@ -178,7 +178,7 @@ class CoreWalletsService {
|
|||
verifyWalletsAreAvailable();
|
||||
verifyEncryptedWalletIsUnlocked();
|
||||
try {
|
||||
return xmrWalletService.getWallet().relayTx(metadata);
|
||||
return xmrWalletService.relayTx(metadata);
|
||||
} catch (Exception ex) {
|
||||
log.error("", ex);
|
||||
throw new IllegalStateException(ex);
|
||||
|
|
|
@ -273,7 +273,11 @@ public final class XmrConnectionService {
|
|||
}
|
||||
|
||||
public synchronized boolean requestSwitchToNextBestConnection() {
|
||||
log.warn("Requesting switch to next best monerod, current monerod={}", getConnection() == null ? null : getConnection().getUri());
|
||||
return requestSwitchToNextBestConnection(null);
|
||||
}
|
||||
|
||||
public synchronized boolean requestSwitchToNextBestConnection(MoneroRpcConnection sourceConnection) {
|
||||
log.warn("Requesting switch to next best monerod, source monerod={}", sourceConnection == null ? getConnection() == null ? null : getConnection().getUri() : sourceConnection.getUri());
|
||||
|
||||
// skip if shut down started
|
||||
if (isShutDownStarted) {
|
||||
|
@ -281,9 +285,15 @@ public final class XmrConnectionService {
|
|||
return false;
|
||||
}
|
||||
|
||||
// skip if connection is already switched
|
||||
if (sourceConnection != null && sourceConnection != getConnection()) {
|
||||
log.warn("Skipping switch to next best Monero connection because source connection is not current connection");
|
||||
return false;
|
||||
}
|
||||
|
||||
// skip if connection is fixed
|
||||
if (isFixedConnection() || !connectionManager.getAutoSwitch()) {
|
||||
log.info("Skipping switch to next best Monero connection because connection is fixed or auto switch is disabled");
|
||||
log.warn("Skipping switch to next best Monero connection because connection is fixed or auto switch is disabled");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -132,35 +132,14 @@ public class WalletAppSetup {
|
|||
(numConnectionUpdates, walletDownloadPercentage, walletHeight, exception, errorMsg) -> {
|
||||
String result;
|
||||
if (exception == null && errorMsg == null) {
|
||||
|
||||
// update wallet sync progress
|
||||
double walletDownloadPercentageD = (double) walletDownloadPercentage;
|
||||
xmrWalletSyncProgress.set(walletDownloadPercentageD);
|
||||
Long bestWalletHeight = walletHeight == null ? null : (Long) walletHeight;
|
||||
String walletHeightAsString = bestWalletHeight != null && bestWalletHeight > 0 ? String.valueOf(bestWalletHeight) : "";
|
||||
if (walletDownloadPercentageD == 1) {
|
||||
String synchronizedWith = Res.get("mainView.footer.xmrInfo.syncedWith", getXmrWalletNetworkAsString(), walletHeightAsString);
|
||||
String feeInfo = ""; // TODO: feeService.isFeeAvailable() returns true, disable
|
||||
result = Res.get("mainView.footer.xmrInfo", synchronizedWith, feeInfo);
|
||||
getXmrSplashSyncIconId().set("image-connection-synced");
|
||||
downloadCompleteHandler.run();
|
||||
} else if (walletDownloadPercentageD > 0) {
|
||||
String synchronizingWith = Res.get("mainView.footer.xmrInfo.synchronizingWalletWith", getXmrWalletNetworkAsString(), walletHeightAsString, FormattingUtils.formatToRoundedPercentWithSymbol(walletDownloadPercentageD));
|
||||
result = Res.get("mainView.footer.xmrInfo", synchronizingWith, "");
|
||||
getXmrSplashSyncIconId().set(""); // clear synced icon
|
||||
} else {
|
||||
|
||||
// update daemon sync progress
|
||||
double chainDownloadPercentageD = xmrConnectionService.downloadPercentageProperty().doubleValue();
|
||||
|
||||
// update daemon sync progress
|
||||
double chainDownloadPercentageD = xmrConnectionService.downloadPercentageProperty().doubleValue();
|
||||
Long bestChainHeight = xmrConnectionService.chainHeightProperty().get();
|
||||
String chainHeightAsString = bestChainHeight != null && bestChainHeight > 0 ? String.valueOf(bestChainHeight) : "";
|
||||
if (chainDownloadPercentageD < 1) {
|
||||
xmrDaemonSyncProgress.set(chainDownloadPercentageD);
|
||||
Long bestChainHeight = xmrConnectionService.chainHeightProperty().get();
|
||||
String chainHeightAsString = bestChainHeight != null && bestChainHeight > 0 ? String.valueOf(bestChainHeight) : "";
|
||||
if (chainDownloadPercentageD == 1) {
|
||||
String synchronizedWith = Res.get("mainView.footer.xmrInfo.connectedTo", getXmrDaemonNetworkAsString(), chainHeightAsString);
|
||||
String feeInfo = ""; // TODO: feeService.isFeeAvailable() returns true, disable
|
||||
result = Res.get("mainView.footer.xmrInfo", synchronizedWith, feeInfo);
|
||||
getXmrSplashSyncIconId().set("image-connection-synced");
|
||||
} else if (chainDownloadPercentageD > 0.0) {
|
||||
if (chainDownloadPercentageD > 0.0) {
|
||||
String synchronizingWith = Res.get("mainView.footer.xmrInfo.synchronizingWith", getXmrDaemonNetworkAsString(), chainHeightAsString, FormattingUtils.formatToRoundedPercentWithSymbol(chainDownloadPercentageD));
|
||||
result = Res.get("mainView.footer.xmrInfo", synchronizingWith, "");
|
||||
} else {
|
||||
|
@ -168,6 +147,29 @@ public class WalletAppSetup {
|
|||
Res.get("mainView.footer.xmrInfo.connectingTo"),
|
||||
getXmrDaemonNetworkAsString());
|
||||
}
|
||||
} else {
|
||||
|
||||
// update wallet sync progress
|
||||
double walletDownloadPercentageD = (double) walletDownloadPercentage;
|
||||
xmrWalletSyncProgress.set(walletDownloadPercentageD);
|
||||
Long bestWalletHeight = walletHeight == null ? null : (Long) walletHeight;
|
||||
String walletHeightAsString = bestWalletHeight != null && bestWalletHeight > 0 ? String.valueOf(bestWalletHeight) : "";
|
||||
if (walletDownloadPercentageD == 1) {
|
||||
String synchronizedWith = Res.get("mainView.footer.xmrInfo.syncedWith", getXmrWalletNetworkAsString(), walletHeightAsString);
|
||||
String feeInfo = ""; // TODO: feeService.isFeeAvailable() returns true, disable
|
||||
result = Res.get("mainView.footer.xmrInfo", synchronizedWith, feeInfo);
|
||||
getXmrSplashSyncIconId().set("image-connection-synced");
|
||||
downloadCompleteHandler.run();
|
||||
} else if (walletDownloadPercentageD >= 0) {
|
||||
String synchronizingWith = Res.get("mainView.footer.xmrInfo.synchronizingWalletWith", getXmrWalletNetworkAsString(), walletHeightAsString, FormattingUtils.formatToRoundedPercentWithSymbol(walletDownloadPercentageD));
|
||||
result = Res.get("mainView.footer.xmrInfo", synchronizingWith, "");
|
||||
getXmrSplashSyncIconId().set(""); // clear synced icon
|
||||
} else {
|
||||
String synchronizedWith = Res.get("mainView.footer.xmrInfo.connectedTo", getXmrDaemonNetworkAsString(), chainHeightAsString);
|
||||
String feeInfo = ""; // TODO: feeService.isFeeAvailable() returns true, disable
|
||||
result = Res.get("mainView.footer.xmrInfo", synchronizedWith, feeInfo);
|
||||
getXmrSplashSyncIconId().set("image-connection-synced");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = Res.get("mainView.footer.xmrInfo",
|
||||
|
|
|
@ -110,6 +110,7 @@ import javafx.collections.FXCollections;
|
|||
import javafx.collections.ObservableList;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.Getter;
|
||||
import monero.common.MoneroRpcConnection;
|
||||
import monero.daemon.model.MoneroKeyImageSpentStatus;
|
||||
import monero.daemon.model.MoneroTx;
|
||||
import monero.wallet.model.MoneroIncomingTransfer;
|
||||
|
@ -1089,6 +1090,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
synchronized (HavenoUtils.getWalletFunctionLock()) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) {
|
||||
MoneroRpcConnection sourceConnection = xmrConnectionService.getConnection();
|
||||
try {
|
||||
log.info("Creating split output tx to fund offer {} at subaddress {}", openOffer.getShortId(), entry.getSubaddressIndex());
|
||||
splitOutputTx = xmrWalletService.createTx(new MoneroTxConfig()
|
||||
|
@ -1101,8 +1103,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
} catch (Exception e) {
|
||||
if (e.getMessage().contains("not enough")) throw e; // do not retry if not enough funds
|
||||
log.warn("Error creating split output tx to fund offer, offerId={}, subaddress={}, attempt={}/{}, error={}", openOffer.getShortId(), entry.getSubaddressIndex(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage());
|
||||
xmrWalletService.handleWalletError(e, sourceConnection);
|
||||
if (stopped || i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;
|
||||
if (xmrConnectionService.isConnected()) xmrWalletService.requestSwitchToNextBestConnection();
|
||||
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import haveno.core.trade.protocol.TradeProtocol;
|
|||
import haveno.core.xmr.model.XmrAddressEntry;
|
||||
import haveno.core.xmr.wallet.XmrWalletService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import monero.common.MoneroRpcConnection;
|
||||
import monero.daemon.model.MoneroOutput;
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
|
||||
|
@ -82,14 +83,16 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
|
|||
try {
|
||||
synchronized (HavenoUtils.getWalletFunctionLock()) {
|
||||
for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) {
|
||||
MoneroRpcConnection sourceConnection = model.getXmrWalletService().getConnectionService().getConnection();
|
||||
try {
|
||||
//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());
|
||||
model.getXmrWalletService().handleWalletError(e, sourceConnection);
|
||||
verifyPending();
|
||||
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;
|
||||
model.getProtocol().startTimeoutTimer(); // reset protocol timeout
|
||||
if (model.getXmrWalletService().getConnectionService().isConnected()) model.getXmrWalletService().requestSwitchToNextBestConnection();
|
||||
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
|
||||
}
|
||||
|
||||
|
@ -129,7 +132,11 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
|
|||
}
|
||||
}
|
||||
|
||||
public void verifyPending() {
|
||||
if (!model.getOpenOffer().isPending()) throw new RuntimeException("Offer " + model.getOpenOffer().getOffer().getId() + " is canceled");
|
||||
private boolean isPending() {
|
||||
return model.getOpenOffer().isPending();
|
||||
}
|
||||
|
||||
private void verifyPending() {
|
||||
if (!isPending()) throw new RuntimeException("Offer " + model.getOpenOffer().getOffer().getId() + " is canceled");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,6 +78,7 @@ import haveno.network.p2p.P2PService;
|
|||
import haveno.network.p2p.network.Connection;
|
||||
import haveno.network.p2p.network.MessageListener;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import monero.common.MoneroRpcConnection;
|
||||
import monero.wallet.MoneroWallet;
|
||||
import monero.wallet.model.MoneroDestination;
|
||||
import monero.wallet.model.MoneroMultisigSignResult;
|
||||
|
@ -501,6 +502,7 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
|
|||
|
||||
// submit fully signed payout tx to the network
|
||||
for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) {
|
||||
MoneroRpcConnection sourceConnection = xmrConnectionService.getConnection();
|
||||
try {
|
||||
List<String> txHashes = multisigWallet.submitMultisigTxHex(disputeTxSet.getMultisigTxHex());
|
||||
disputeTxSet.getTxs().get(0).setHash(txHashes.get(0)); // manually update hash which is known after signed
|
||||
|
@ -509,7 +511,7 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
|
|||
if (trade.isPayoutPublished()) throw new IllegalStateException("Payout tx already published for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
|
||||
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();
|
||||
if (trade.getXmrConnectionService().isConnected()) trade.requestSwitchToNextBestConnection(sourceConnection);
|
||||
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
|
||||
}
|
||||
}
|
||||
|
|
|
@ -506,4 +506,16 @@ public class HavenoUtils {
|
|||
public static void setTopError(String msg) {
|
||||
havenoSetup.getTopErrorMsg().set(msg);
|
||||
}
|
||||
|
||||
public static boolean isConnectionRefused(Exception e) {
|
||||
return e != null && e.getMessage().contains("Connection refused");
|
||||
}
|
||||
|
||||
public static boolean isReadTimeout(Exception e) {
|
||||
return e != null && e.getMessage().contains("Read timed out");
|
||||
}
|
||||
|
||||
public static boolean isUnresponsive(Exception e) {
|
||||
return isConnectionRefused(e) || isReadTimeout(e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -850,8 +850,8 @@ public abstract class Trade implements Tradable, Model {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean requestSwitchToNextBestConnection() {
|
||||
if (xmrConnectionService.requestSwitchToNextBestConnection()) {
|
||||
public boolean requestSwitchToNextBestConnection(MoneroRpcConnection sourceConnection) {
|
||||
if (xmrConnectionService.requestSwitchToNextBestConnection(sourceConnection)) {
|
||||
onConnectionChanged(xmrConnectionService.getConnection()); // change connection on same thread
|
||||
return true;
|
||||
}
|
||||
|
@ -895,10 +895,6 @@ public abstract class Trade implements Tradable, Model {
|
|||
}).start();
|
||||
}
|
||||
|
||||
private boolean isReadTimeoutError(String errMsg) {
|
||||
return errMsg.contains("Read timed out");
|
||||
}
|
||||
|
||||
// TODO: checking error strings isn't robust, but the library doesn't provide a way to check if multisig hex is invalid. throw IllegalArgumentException from library on invalid multisig hex?
|
||||
private boolean isInvalidImportError(String errMsg) {
|
||||
return errMsg.contains("Failed to parse hex") || errMsg.contains("Multisig info is for a different account");
|
||||
|
@ -1081,6 +1077,7 @@ public abstract class Trade implements Tradable, Model {
|
|||
synchronized (walletLock) {
|
||||
synchronized (HavenoUtils.getDaemonLock()) { // lock on daemon because import calls full refresh
|
||||
for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) {
|
||||
MoneroRpcConnection sourceConnection = xmrConnectionService.getConnection();
|
||||
try {
|
||||
doImportMultisigHex();
|
||||
break;
|
||||
|
@ -1088,9 +1085,8 @@ public abstract class Trade implements Tradable, Model {
|
|||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to import multisig hex, tradeId={}, attempt={}/{}, error={}", getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage());
|
||||
handleWalletError(e, sourceConnection);
|
||||
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;
|
||||
if (xmrConnectionService.isConnected()) requestSwitchToNextBestConnection();
|
||||
if (isReadTimeoutError(e.getMessage())) forceRestartTradeWallet(); // wallet can be stuck a while
|
||||
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
|
||||
}
|
||||
}
|
||||
|
@ -1168,6 +1164,12 @@ public abstract class Trade implements Tradable, Model {
|
|||
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);
|
||||
getWallet(); // re-open wallet
|
||||
}
|
||||
|
||||
private String getMultisigHexRole(String multisigHex) {
|
||||
if (multisigHex.equals(getArbitrator().getUpdatedMultisigHex())) return "arbitrator";
|
||||
if (multisigHex.equals(getBuyer().getUpdatedMultisigHex())) return "buyer";
|
||||
|
@ -1189,14 +1191,15 @@ public abstract class Trade implements Tradable, Model {
|
|||
synchronized (walletLock) {
|
||||
synchronized (HavenoUtils.getWalletFunctionLock()) {
|
||||
for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) {
|
||||
MoneroRpcConnection sourceConnection = xmrConnectionService.getConnection();
|
||||
try {
|
||||
return doCreatePayoutTx();
|
||||
} 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());
|
||||
handleWalletError(e, sourceConnection);
|
||||
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;
|
||||
if (xmrConnectionService.isConnected()) requestSwitchToNextBestConnection();
|
||||
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
|
||||
}
|
||||
}
|
||||
|
@ -1246,6 +1249,7 @@ public abstract class Trade implements Tradable, Model {
|
|||
synchronized (walletLock) {
|
||||
synchronized (HavenoUtils.getWalletFunctionLock()) {
|
||||
for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) {
|
||||
MoneroRpcConnection sourceConnection = xmrConnectionService.getConnection();
|
||||
try {
|
||||
if (wallet.isMultisigImportNeeded()) throw new IllegalStateException("Cannot create dispute payout tx because multisig import is needed for " + getClass().getSimpleName() + " " + getShortId());
|
||||
return createTx(txConfig);
|
||||
|
@ -1254,8 +1258,8 @@ public abstract class Trade implements Tradable, Model {
|
|||
} 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());
|
||||
handleWalletError(e, sourceConnection);
|
||||
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;
|
||||
if (xmrConnectionService.isConnected()) requestSwitchToNextBestConnection();
|
||||
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
|
||||
}
|
||||
}
|
||||
|
@ -1275,6 +1279,7 @@ public abstract class Trade implements Tradable, Model {
|
|||
synchronized (walletLock) {
|
||||
synchronized (HavenoUtils.getWalletFunctionLock()) {
|
||||
for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) {
|
||||
MoneroRpcConnection sourceConnection = xmrConnectionService.getConnection();
|
||||
try {
|
||||
doProcessPayoutTx(payoutTxHex, sign, publish);
|
||||
break;
|
||||
|
@ -1282,8 +1287,8 @@ public abstract class Trade implements Tradable, Model {
|
|||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to process payout tx, attempt={}/{}, tradeId={}, error={}", i + 1, TradeProtocol.MAX_ATTEMPTS, getShortId(), e.getMessage());
|
||||
handleWalletError(e, sourceConnection);
|
||||
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;
|
||||
if (xmrConnectionService.isConnected()) requestSwitchToNextBestConnection();
|
||||
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
|
||||
} finally {
|
||||
requestSaveWallet();
|
||||
|
@ -2412,6 +2417,7 @@ public abstract class Trade implements Tradable, Model {
|
|||
}
|
||||
|
||||
private void syncWallet(boolean pollWallet) {
|
||||
MoneroRpcConnection sourceConnection = xmrConnectionService.getConnection();
|
||||
try {
|
||||
if (getWallet() == null) throw new RuntimeException("Cannot sync trade wallet because it doesn't exist for " + getClass().getSimpleName() + ", " + getId());
|
||||
if (getWallet().getDaemonConnection() == null) throw new RuntimeException("Cannot sync trade wallet because it's not connected to a Monero daemon for " + getClass().getSimpleName() + ", " + getId());
|
||||
|
@ -2432,7 +2438,7 @@ public abstract class Trade implements Tradable, Model {
|
|||
|
||||
if (pollWallet) pollWallet();
|
||||
} catch (Exception e) {
|
||||
ThreadUtils.execute(() -> requestSwitchToNextBestConnection(), getId());
|
||||
ThreadUtils.execute(() -> requestSwitchToNextBestConnection(sourceConnection), getId());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
@ -2561,11 +2567,12 @@ public abstract class Trade implements Tradable, Model {
|
|||
|
||||
// rescan spent outputs to detect unconfirmed payout tx
|
||||
if (isPayoutExpected && wallet.getBalance().compareTo(BigInteger.ZERO) > 0) {
|
||||
MoneroRpcConnection sourceConnection = xmrConnectionService.getConnection();
|
||||
try {
|
||||
wallet.rescanSpent();
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to rescan spent outputs for {} {}, errorMessage={}", getClass().getSimpleName(), getShortId(), e.getMessage());
|
||||
ThreadUtils.execute(() -> requestSwitchToNextBestConnection(), getId()); // do not block polling thread
|
||||
ThreadUtils.execute(() -> requestSwitchToNextBestConnection(sourceConnection), getId()); // do not block polling thread
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2610,12 +2617,11 @@ public abstract class Trade implements Tradable, Model {
|
|||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
boolean isConnectionRefused = e.getMessage() != null && e.getMessage().contains("Connection refused");
|
||||
if (isConnectionRefused) forceRestartTradeWallet();
|
||||
if (HavenoUtils.isUnresponsive(e)) forceRestartTradeWallet();
|
||||
else {
|
||||
boolean isWalletConnected = isWalletConnectedToDaemon();
|
||||
if (wallet != null && !isShutDownStarted && isWalletConnected) {
|
||||
log.warn("Error polling trade wallet for {} {}, errorMessage={}. Monerod={}", getClass().getSimpleName(), getShortId(), e.getMessage(), getXmrWalletService().getConnectionService().getConnection());
|
||||
log.warn("Error polling trade wallet for {} {}, errorMessage={}. Monerod={}", getClass().getSimpleName(), getShortId(), e.getMessage(), wallet.getDaemonConnection());
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
@ -2675,7 +2681,8 @@ public abstract class Trade implements Tradable, Model {
|
|||
log.warn("Rescanning blockchain for {} {}", getClass().getSimpleName(), getShortId());
|
||||
wallet.rescanBlockchain();
|
||||
} catch (Exception e) {
|
||||
if (isReadTimeoutError(e.getMessage())) forceRestartTradeWallet(); // wallet can be stuck a while
|
||||
log.warn("Error rescanning blockchain for {} {}, errorMessage={}", getClass().getSimpleName(), getShortId(), e.getMessage());
|
||||
if (HavenoUtils.isUnresponsive(e)) forceRestartTradeWallet(); // wallet can be stuck a while
|
||||
throw e;
|
||||
} finally {
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import haveno.core.xmr.model.XmrAddressEntry;
|
|||
import haveno.core.xmr.wallet.XmrWalletService;
|
||||
import haveno.network.p2p.SendDirectMessageListener;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import monero.common.MoneroRpcConnection;
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
@ -100,12 +101,14 @@ public class MaybeSendSignContractRequest extends TradeTask {
|
|||
try {
|
||||
synchronized (HavenoUtils.getWalletFunctionLock()) {
|
||||
for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) {
|
||||
MoneroRpcConnection sourceConnection = trade.getXmrConnectionService().getConnection();
|
||||
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());
|
||||
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;
|
||||
if (trade.getXmrConnectionService().isConnected()) trade.getXmrWalletService().requestSwitchToNextBestConnection();
|
||||
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import haveno.core.trade.protocol.TradeProtocol;
|
|||
import haveno.core.xmr.model.XmrAddressEntry;
|
||||
import haveno.core.xmr.wallet.XmrWalletService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import monero.common.MoneroRpcConnection;
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
@ -66,12 +67,14 @@ public class TakerReserveTradeFunds extends TradeTask {
|
|||
try {
|
||||
synchronized (HavenoUtils.getWalletFunctionLock()) {
|
||||
for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) {
|
||||
MoneroRpcConnection sourceConnection = trade.getXmrConnectionService().getConnection();
|
||||
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());
|
||||
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;
|
||||
if (trade.getXmrConnectionService().isConnected()) trade.getXmrWalletService().requestSwitchToNextBestConnection();
|
||||
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
|
||||
}
|
||||
|
||||
|
|
|
@ -160,9 +160,11 @@ public class XmrWalletService {
|
|||
private boolean isClosingWallet;
|
||||
private boolean isShutDownStarted;
|
||||
private ExecutorService syncWalletThreadPool = Executors.newFixedThreadPool(10); // TODO: adjust based on connection type
|
||||
private boolean isSyncingWithProgress;
|
||||
private Long syncStartHeight;
|
||||
private TaskLooper syncProgressLooper;
|
||||
private CountDownLatch syncProgressLatch;
|
||||
private Exception syncProgressError;
|
||||
private Timer syncProgressTimeout;
|
||||
private static final int SYNC_PROGRESS_TIMEOUT_SECONDS = 60;
|
||||
|
||||
|
@ -178,7 +180,9 @@ public class XmrWalletService {
|
|||
private List<MoneroSubaddress> cachedSubaddresses;
|
||||
private List<MoneroOutputWallet> cachedOutputs;
|
||||
private List<MoneroTxWallet> cachedTxs;
|
||||
private boolean runReconnectTestOnStartup = false; // test reconnecting on startup while syncing so the wallet is blocked
|
||||
private boolean testReconnectOnStartup = false; // test reconnecting on startup while syncing so the wallet is blocked
|
||||
private String testReconnectMonerod1 = "http://node.community.rino.io:18081";
|
||||
private String testReconnectMonerod2 = "http://nodex.monerujo.io:18081";
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Inject
|
||||
|
@ -473,6 +477,14 @@ public class XmrWalletService {
|
|||
}
|
||||
}
|
||||
|
||||
public String relayTx(String metadata) {
|
||||
synchronized (WALLET_LOCK) {
|
||||
String txId = wallet.relayTx(metadata);
|
||||
requestSaveMainWallet();
|
||||
return txId;
|
||||
}
|
||||
}
|
||||
|
||||
public MoneroTxWallet createTx(List<MoneroDestination> destinations) {
|
||||
MoneroTxWallet tx = createTx(new MoneroTxConfig().setAccountIndex(0).setDestinations(destinations).setRelay(false).setCanSplit(false));
|
||||
//printTxs("XmrWalletService.createTx", tx);
|
||||
|
@ -1289,9 +1301,19 @@ public class XmrWalletService {
|
|||
else log.info(appliedMsg);
|
||||
|
||||
// listen for connection changes
|
||||
xmrConnectionService.addConnectionListener(connection -> ThreadUtils.execute(() -> {
|
||||
onConnectionChanged(connection);
|
||||
}, THREAD_ID));
|
||||
xmrConnectionService.addConnectionListener(connection -> {
|
||||
if (wasWalletSynced && !isSyncingWithProgress) {
|
||||
ThreadUtils.execute(() -> {
|
||||
onConnectionChanged(connection);
|
||||
}, THREAD_ID);
|
||||
} else {
|
||||
|
||||
// force restart main wallet if connection changed while syncing
|
||||
log.warn("Force restarting main wallet because connection changed while syncing");
|
||||
forceRestartMainWallet();
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// initialize main wallet when daemon synced
|
||||
walletInitListener = (obs, oldVal, newVal) -> initMainWalletIfConnected();
|
||||
|
@ -1340,6 +1362,7 @@ public class XmrWalletService {
|
|||
long date = localDateTime.toEpochSecond(ZoneOffset.UTC);
|
||||
user.setWalletCreationDate(date);
|
||||
}
|
||||
walletHeight.set(wallet.getHeight());
|
||||
isClosingWallet = false;
|
||||
}
|
||||
|
||||
|
@ -1348,6 +1371,7 @@ public class XmrWalletService {
|
|||
log.info("Monero wallet path={}", wallet.getPath());
|
||||
|
||||
// sync main wallet if applicable
|
||||
// TODO: error handling and re-initialization is jenky, refactor
|
||||
if (sync && numAttempts > 0) {
|
||||
try {
|
||||
|
||||
|
@ -1360,7 +1384,16 @@ public class XmrWalletService {
|
|||
// sync main wallet
|
||||
log.info("Syncing main wallet");
|
||||
long time = System.currentTimeMillis();
|
||||
syncWithProgress(); // blocking
|
||||
MoneroRpcConnection sourceConnection = xmrConnectionService.getConnection();
|
||||
try {
|
||||
syncWithProgress(); // blocking
|
||||
} catch (Exception e) {
|
||||
log.warn("Error syncing wallet with progress on startup: " + e.getMessage());
|
||||
forceCloseMainWallet();
|
||||
requestSwitchToNextBestConnection(sourceConnection);
|
||||
maybeInitMainWallet(true, numAttempts - 1); // re-initialize wallet and sync again
|
||||
return;
|
||||
}
|
||||
log.info("Done syncing main wallet in " + (System.currentTimeMillis() - time) + " ms");
|
||||
|
||||
// poll wallet
|
||||
|
@ -1435,69 +1468,83 @@ public class XmrWalletService {
|
|||
}
|
||||
|
||||
private void syncWithProgress() {
|
||||
synchronized (WALLET_LOCK) {
|
||||
|
||||
// start sync progress timeout
|
||||
resetSyncProgressTimeout();
|
||||
// set initial state
|
||||
isSyncingWithProgress = true;
|
||||
syncProgressError = null;
|
||||
updateSyncProgress(walletHeight.get());
|
||||
|
||||
// show sync progress
|
||||
updateSyncProgress(wallet.getHeight());
|
||||
// test connection changing on startup before wallet synced
|
||||
if (testReconnectOnStartup) {
|
||||
UserThread.runAfter(() -> {
|
||||
log.warn("Testing connection change on startup before wallet synced");
|
||||
if (xmrConnectionService.getConnection().getUri().equals(testReconnectMonerod1)) xmrConnectionService.setConnection(testReconnectMonerod2);
|
||||
else xmrConnectionService.setConnection(testReconnectMonerod1);
|
||||
}, 1);
|
||||
testReconnectOnStartup = false; // only run once
|
||||
}
|
||||
|
||||
// test connection changing on startup before wallet synced
|
||||
if (runReconnectTestOnStartup) {
|
||||
UserThread.runAfter(() -> {
|
||||
log.warn("Testing connection change on startup before wallet synced");
|
||||
xmrConnectionService.setConnection("http://node.community.rino.io:18081"); // TODO: needs to be online
|
||||
}, 1);
|
||||
runReconnectTestOnStartup = false; // only run once
|
||||
}
|
||||
|
||||
// get sync notifications from native wallet
|
||||
if (wallet instanceof MoneroWalletFull) {
|
||||
if (runReconnectTestOnStartup) HavenoUtils.waitFor(1000); // delay sync to test
|
||||
wallet.sync(new MoneroWalletListener() {
|
||||
@Override
|
||||
public void onSyncProgress(long height, long startHeight, long endHeight, double percentDone, String message) {
|
||||
updateSyncProgress(height);
|
||||
}
|
||||
});
|
||||
wasWalletSynced = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// poll wallet for progress
|
||||
wallet.startSyncing(xmrConnectionService.getRefreshPeriodMs());
|
||||
syncProgressLatch = new CountDownLatch(1);
|
||||
syncProgressLooper = new TaskLooper(() -> {
|
||||
if (wallet == null) return;
|
||||
long height = 0;
|
||||
try {
|
||||
height = wallet.getHeight(); // can get read timeout while syncing
|
||||
} catch (Exception e) {
|
||||
if (!isShutDownStarted) e.printStackTrace();
|
||||
// native wallet provides sync notifications
|
||||
if (wallet instanceof MoneroWalletFull) {
|
||||
if (testReconnectOnStartup) HavenoUtils.waitFor(1000); // delay sync to test
|
||||
wallet.sync(new MoneroWalletListener() {
|
||||
@Override
|
||||
public void onSyncProgress(long height, long startHeight, long endHeight, double percentDone, String message) {
|
||||
updateSyncProgress(height);
|
||||
}
|
||||
});
|
||||
setWalletSyncedWithProgress();
|
||||
return;
|
||||
}
|
||||
if (height < xmrConnectionService.getTargetHeight()) updateSyncProgress(height);
|
||||
else {
|
||||
syncProgressLooper.stop();
|
||||
wasWalletSynced = true;
|
||||
|
||||
// start polling wallet for progress
|
||||
syncProgressLatch = new CountDownLatch(1);
|
||||
syncProgressLooper = new TaskLooper(() -> {
|
||||
if (wallet == null) return;
|
||||
long height;
|
||||
try {
|
||||
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();
|
||||
|
||||
// stop polling and release latch
|
||||
syncProgressError = e;
|
||||
syncProgressLatch.countDown();
|
||||
return;
|
||||
}
|
||||
updateSyncProgress(height);
|
||||
syncProgressLatch.countDown();
|
||||
}
|
||||
});
|
||||
syncProgressLooper.start(1000);
|
||||
HavenoUtils.awaitLatch(syncProgressLatch);
|
||||
wallet.stopSyncing();
|
||||
if (!wasWalletSynced) throw new IllegalStateException("Failed to sync wallet with progress");
|
||||
if (height >= xmrConnectionService.getTargetHeight()) {
|
||||
setWalletSyncedWithProgress();
|
||||
syncProgressLatch.countDown();
|
||||
}
|
||||
});
|
||||
wallet.startSyncing(xmrConnectionService.getRefreshPeriodMs());
|
||||
syncProgressLooper.start(1000);
|
||||
|
||||
// wait for sync to complete
|
||||
HavenoUtils.awaitLatch(syncProgressLatch);
|
||||
|
||||
// stop polling
|
||||
syncProgressLooper.stop();
|
||||
syncProgressTimeout.stop();
|
||||
if (wallet != null) wallet.stopSyncing(); // can become null if interrupted by force close
|
||||
isSyncingWithProgress = false;
|
||||
if (syncProgressError != null) throw new RuntimeException(syncProgressError);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSyncProgress(long height) {
|
||||
resetSyncProgressTimeout();
|
||||
UserThread.execute(() -> {
|
||||
|
||||
// set wallet height
|
||||
walletHeight.set(height);
|
||||
resetSyncProgressTimeout();
|
||||
|
||||
// new wallet reports height 1 before synced
|
||||
if (height == 1) {
|
||||
downloadListener.progress(.0001, xmrConnectionService.getTargetHeight() - height, null); // >0% shows progress bar
|
||||
downloadListener.progress(0, xmrConnectionService.getTargetHeight() - height, null);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1505,7 +1552,7 @@ public class XmrWalletService {
|
|||
long targetHeight = xmrConnectionService.getTargetHeight();
|
||||
long blocksLeft = targetHeight - walletHeight.get();
|
||||
if (syncStartHeight == null) syncStartHeight = walletHeight.get();
|
||||
double percent = Math.min(1.0, targetHeight == syncStartHeight ? 1.0 : ((double) Math.max(1, (double) walletHeight.get() - syncStartHeight) / (double) (targetHeight - syncStartHeight))); // grant at least 1 block to show progress
|
||||
double percent = Math.min(1.0, targetHeight == syncStartHeight ? 1.0 : ((double) walletHeight.get() - syncStartHeight) / (double) (targetHeight - syncStartHeight));
|
||||
downloadListener.progress(percent, blocksLeft, null);
|
||||
});
|
||||
}
|
||||
|
@ -1513,15 +1560,18 @@ public class XmrWalletService {
|
|||
private synchronized void resetSyncProgressTimeout() {
|
||||
if (syncProgressTimeout != null) syncProgressTimeout.stop();
|
||||
syncProgressTimeout = UserThread.runAfter(() -> {
|
||||
if (isShutDownStarted || wasWalletSynced) return;
|
||||
log.warn("Sync progress timeout called");
|
||||
forceCloseMainWallet();
|
||||
requestSwitchToNextBestConnection();
|
||||
maybeInitMainWallet(true);
|
||||
resetSyncProgressTimeout();
|
||||
if (isShutDownStarted) return;
|
||||
syncProgressError = new RuntimeException("Sync progress timeout called");
|
||||
syncProgressLatch.countDown();
|
||||
}, SYNC_PROGRESS_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void setWalletSyncedWithProgress() {
|
||||
wasWalletSynced = true;
|
||||
isSyncingWithProgress = false;
|
||||
syncProgressTimeout.stop();
|
||||
}
|
||||
|
||||
private MoneroWalletFull createWalletFull(MoneroWalletConfig config) {
|
||||
|
||||
// must be connected to daemon
|
||||
|
@ -1686,14 +1736,6 @@ public class XmrWalletService {
|
|||
String newProxyUri = connection == null ? null : connection.getProxyUri();
|
||||
log.info("Setting daemon connection for main wallet, monerod={}, proxyUri={}", connection == null ? null : connection.getUri(), newProxyUri);
|
||||
|
||||
// force restart main wallet if connection changed before synced
|
||||
if (!wasWalletSynced) {
|
||||
if (!Boolean.TRUE.equals(xmrConnectionService.isConnected())) return;
|
||||
log.warn("Force restarting main wallet because connection changed before inital sync");
|
||||
forceRestartMainWallet();
|
||||
return;
|
||||
}
|
||||
|
||||
// update connection
|
||||
if (wallet instanceof MoneroWalletRpc) {
|
||||
if (StringUtils.equals(oldProxyUri, newProxyUri)) {
|
||||
|
@ -1702,7 +1744,7 @@ public class XmrWalletService {
|
|||
log.info("Restarting main wallet because proxy URI has changed, old={}, new={}", oldProxyUri, newProxyUri); // TODO: set proxy without restarting wallet
|
||||
closeMainWallet(true);
|
||||
doMaybeInitMainWallet(false, MAX_SYNC_ATTEMPTS);
|
||||
return; // wallet is re-initialized
|
||||
return; // wallet re-initializes off thread
|
||||
}
|
||||
} else {
|
||||
wallet.setDaemonConnection(connection);
|
||||
|
@ -1771,19 +1813,26 @@ public class XmrWalletService {
|
|||
|
||||
private void forceCloseMainWallet() {
|
||||
stopPolling();
|
||||
if (wallet != null) {
|
||||
if (wallet != null && !isClosingWallet) {
|
||||
isClosingWallet = true;
|
||||
forceCloseWallet(wallet, getWalletPath(MONERO_WALLET_NAME));
|
||||
wallet = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void forceRestartMainWallet() {
|
||||
public void forceRestartMainWallet() {
|
||||
log.warn("Force restarting main wallet");
|
||||
if (isClosingWallet) return;
|
||||
forceCloseMainWallet();
|
||||
maybeInitMainWallet(true);
|
||||
}
|
||||
|
||||
public void handleWalletError(Exception e, MoneroRpcConnection sourceConnection) {
|
||||
if (HavenoUtils.isUnresponsive(e)) forceCloseMainWallet(); // wallet can be stuck a while
|
||||
if (xmrConnectionService.isConnected()) requestSwitchToNextBestConnection(sourceConnection);
|
||||
getWallet(); // re-open wallet
|
||||
}
|
||||
|
||||
private void startPolling() {
|
||||
synchronized (WALLET_LOCK) {
|
||||
if (isShutDownStarted || isPolling()) return;
|
||||
|
@ -1849,17 +1898,10 @@ public class XmrWalletService {
|
|||
log.warn("Monero daemon is not synced within tolerance, height={}, targetHeight={}", xmrConnectionService.chainHeightProperty().get(), xmrConnectionService.getTargetHeight());
|
||||
return;
|
||||
}
|
||||
|
||||
// switch to best connection if wallet is too far behind
|
||||
if (wasWalletSynced && walletHeight.get() < xmrConnectionService.getTargetHeight() - NUM_BLOCKS_BEHIND_TOLERANCE && !Config.baseCurrencyNetwork().isTestnet()) {
|
||||
log.warn("Updating connection because main wallet is {} blocks behind monerod, wallet height={}, monerod height={}", xmrConnectionService.getTargetHeight() - walletHeight.get(), walletHeight.get(), lastInfo.getHeight());
|
||||
if (xmrConnectionService.isConnected()) requestSwitchToNextBestConnection();
|
||||
}
|
||||
|
||||
// sync wallet if behind daemon
|
||||
if (walletHeight.get() < xmrConnectionService.getTargetHeight()) {
|
||||
synchronized (WALLET_LOCK) { // avoid long sync from blocking other operations
|
||||
syncMainWallet();
|
||||
syncWithProgress();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1868,13 +1910,17 @@ public class XmrWalletService {
|
|||
if (updateTxs) {
|
||||
synchronized (WALLET_LOCK) { // avoid long fetch from blocking other operations
|
||||
synchronized (HavenoUtils.getDaemonLock()) {
|
||||
MoneroRpcConnection sourceConnection = xmrConnectionService.getConnection();
|
||||
try {
|
||||
cachedTxs = wallet.getTxs(new MoneroTxQuery().setIncludeOutputs(true));
|
||||
} catch (Exception e) { // fetch from pool can fail
|
||||
if (!isShutDownStarted) {
|
||||
if (lastLogPollErrorTimestamp == null || System.currentTimeMillis() - lastLogPollErrorTimestamp > HavenoUtils.LOG_POLL_ERROR_PERIOD_MS) { // limit error logging
|
||||
|
||||
// throttle error handling
|
||||
if (lastLogPollErrorTimestamp == null || System.currentTimeMillis() - lastLogPollErrorTimestamp > HavenoUtils.LOG_POLL_ERROR_PERIOD_MS) {
|
||||
log.warn("Error polling main wallet's transactions from the pool: {}", e.getMessage());
|
||||
lastLogPollErrorTimestamp = System.currentTimeMillis();
|
||||
requestSwitchToNextBestConnection(sourceConnection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1883,8 +1929,7 @@ public class XmrWalletService {
|
|||
}
|
||||
} catch (Exception e) {
|
||||
if (wallet == null || isShutDownStarted) return;
|
||||
boolean isConnectionRefused = e.getMessage() != null && e.getMessage().contains("Connection refused");
|
||||
if (isConnectionRefused) forceRestartMainWallet();
|
||||
if (HavenoUtils.isUnresponsive(e)) forceRestartMainWallet();
|
||||
else if (isWalletConnectedToDaemon()) {
|
||||
log.warn("Error polling main wallet, errorMessage={}. Monerod={}", e.getMessage(), getConnectionService().getConnection());
|
||||
//e.printStackTrace();
|
||||
|
@ -1927,8 +1972,12 @@ public class XmrWalletService {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean requestSwitchToNextBestConnection() {
|
||||
return xmrConnectionService.requestSwitchToNextBestConnection();
|
||||
private boolean requestSwitchToNextBestConnection() {
|
||||
return requestSwitchToNextBestConnection(null);
|
||||
}
|
||||
|
||||
public boolean requestSwitchToNextBestConnection(MoneroRpcConnection sourceConnection) {
|
||||
return xmrConnectionService.requestSwitchToNextBestConnection(sourceConnection);
|
||||
}
|
||||
|
||||
private void onNewBlock(long height) {
|
||||
|
|
|
@ -65,6 +65,7 @@ import javafx.scene.control.Button;
|
|||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import monero.common.MoneroRpcConnection;
|
||||
import monero.common.MoneroUtils;
|
||||
import monero.wallet.model.MoneroTxConfig;
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
|
@ -256,6 +257,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
// create tx
|
||||
MoneroTxWallet tx = null;
|
||||
for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) {
|
||||
MoneroRpcConnection sourceConnection = xmrWalletService.getConnectionService().getConnection();
|
||||
try {
|
||||
log.info("Creating withdraw tx");
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
@ -270,7 +272,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
if (isNotEnoughMoney(e.getMessage())) throw e;
|
||||
log.warn("Error creating creating withdraw tx, attempt={}/{}, error={}", i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage());
|
||||
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;
|
||||
if (xmrWalletService.getConnectionService().isConnected()) xmrWalletService.requestSwitchToNextBestConnection();
|
||||
if (xmrWalletService.getConnectionService().isConnected()) xmrWalletService.requestSwitchToNextBestConnection(sourceConnection);
|
||||
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue