Skip to content

Commit

Permalink
switch to next best monerod on various errors
Browse files Browse the repository at this point in the history
  • Loading branch information
woodser committed Jul 17, 2024
1 parent 3f73e11 commit 1df76ae
Show file tree
Hide file tree
Showing 11 changed files with 664 additions and 445 deletions.
1 change: 1 addition & 0 deletions common/src/main/java/haveno/common/ThreadUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public static Future<?> execute(Runnable command, String threadId) {
synchronized (THREADS) {
THREADS.put(threadId, Thread.currentThread());
}
Thread.currentThread().setName(threadId);
command.run();
});
}
Expand Down
75 changes: 67 additions & 8 deletions core/src/main/java/haveno/core/api/XmrConnectionService.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@
import haveno.network.p2p.P2PService;
import haveno.network.p2p.P2PServiceListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.LongProperty;
Expand Down Expand Up @@ -103,6 +106,12 @@ public final class XmrConnectionService {
private boolean isShutDownStarted;
private List<MoneroConnectionManagerListener> listeners = new ArrayList<>();

// connection switching
private static final int EXCLUDE_CONNECTION_SECONDS = 300;
private static final int SKIP_SWITCH_WITHIN_MS = 60000;
private Set<MoneroRpcConnection> excludedConnections = new HashSet<>();
private long lastSwitchRequestTimestamp;

@Inject
public XmrConnectionService(P2PService p2PService,
Config config,
Expand Down Expand Up @@ -201,12 +210,6 @@ public List<MoneroRpcConnection> getConnections() {
return connectionManager.getConnections();
}

public void switchToBestConnection() {
if (isFixedConnection() || !connectionManager.getAutoSwitch()) return;
MoneroRpcConnection bestConnection = getBestAvailableConnection();
if (bestConnection != null) setConnection(bestConnection);
}

public void setConnection(String connectionUri) {
accountService.checkAccountOpen();
connectionManager.setConnection(connectionUri); // listener will update connection list
Expand Down Expand Up @@ -244,10 +247,67 @@ public void stopCheckingConnection() {
public MoneroRpcConnection getBestAvailableConnection() {
accountService.checkAccountOpen();
List<MoneroRpcConnection> ignoredConnections = new ArrayList<MoneroRpcConnection>();
if (xmrLocalNode.shouldBeIgnored() && connectionManager.hasConnection(xmrLocalNode.getUri())) ignoredConnections.add(connectionManager.getConnectionByUri(xmrLocalNode.getUri()));
addLocalNodeIfIgnored(ignoredConnections);
return connectionManager.getBestAvailableConnection(ignoredConnections.toArray(new MoneroRpcConnection[0]));
}

private MoneroRpcConnection getBestAvailableConnection(Collection<MoneroRpcConnection> ignoredConnections) {
accountService.checkAccountOpen();
Set<MoneroRpcConnection> ignoredConnectionsSet = new HashSet<>(ignoredConnections);
addLocalNodeIfIgnored(ignoredConnectionsSet);
return connectionManager.getBestAvailableConnection(ignoredConnectionsSet.toArray(new MoneroRpcConnection[0]));
}

private void addLocalNodeIfIgnored(Collection<MoneroRpcConnection> ignoredConnections) {
if (xmrLocalNode.shouldBeIgnored() && connectionManager.hasConnection(xmrLocalNode.getUri())) ignoredConnections.add(connectionManager.getConnectionByUri(xmrLocalNode.getUri()));
}

private void switchToBestConnection() {
if (isFixedConnection() || !connectionManager.getAutoSwitch()) {
log.info("Skipping switch to best Monero connection because connection is fixed or auto switch is disabled");
return;
}
MoneroRpcConnection bestConnection = getBestAvailableConnection();
if (bestConnection != null) setConnection(bestConnection);
}

public boolean requestSwitchToNextBestConnection() {
log.warn("Request made to switch to next best monerod, current monerod={}", getConnection() == null ? null : getConnection().getUri());

// 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");
return false;
}

// skip if last switch was too recent
boolean skipSwitch = System.currentTimeMillis() - lastSwitchRequestTimestamp < SKIP_SWITCH_WITHIN_MS;
lastSwitchRequestTimestamp = System.currentTimeMillis();
if (skipSwitch) {
log.warn("Skipping switch to next best Monero connection because last switch was less than {} seconds ago", SKIP_SWITCH_WITHIN_MS / 1000);
lastSwitchRequestTimestamp = System.currentTimeMillis();
return false;
}

// try to get connection to switch to
MoneroRpcConnection currentConnection = getConnection();
if (currentConnection != null) excludedConnections.add(currentConnection);
MoneroRpcConnection bestConnection = getBestAvailableConnection(excludedConnections);

// remove from excluded connections after period
UserThread.runAfter(() -> {
if (currentConnection != null) excludedConnections.remove(currentConnection);
}, EXCLUDE_CONNECTION_SECONDS);

// switch to best connection
if (bestConnection == null) {
log.warn("Could not get connection to switch to");
return false;
}
setConnection(bestConnection);
return true;
}

public void setAutoSwitch(boolean autoSwitch) {
accountService.checkAccountOpen();
connectionManager.setAutoSwitch(autoSwitch);
Expand Down Expand Up @@ -505,7 +565,6 @@ public void onConnectionChanged(MoneroRpcConnection connection) {

// register connection listener
connectionManager.addListener(this::onConnectionChanged);

isInitialized = true;
}

Expand Down
1 change: 1 addition & 0 deletions core/src/main/java/haveno/core/offer/OpenOfferManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -1057,6 +1057,7 @@ private MoneroTxWallet splitAndSchedule(OpenOffer openOffer) {
} catch (Exception e) {
log.warn("Error creating split output tx to fund offer {} at subaddress {}, attempt={}/{}, error={}", openOffer.getShortId(), entry.getSubaddressIndex(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage());
if (stopped || i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;
if (xmrConnectionService.isConnected()) xmrWalletService.requestSwitchToNextBestConnection();
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ protected void run() {
log.warn("Error creating reserve tx, attempt={}/{}, offerId={}, error={}", i + 1, TradeProtocol.MAX_ATTEMPTS, openOffer.getShortId(), e.getMessage());
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
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,7 @@ private MoneroTxSet signAndPublishDisputePayoutTx(Trade trade) {
if (trade.isPayoutPublished()) throw new IllegalStateException("Payout tx already published for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
log.warn("Failed to submit dispute payout tx, attempt={}/{}, tradeId={}, error={}", i + 1, TradeProtocol.MAX_ATTEMPTS, trade.getShortId(), e.getMessage());
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;
if (trade.getXmrConnectionService().isConnected()) trade.requestSwitchToNextBestConnection();
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
}
}
Expand Down
Loading

0 comments on commit 1df76ae

Please sign in to comment.