refactor monero connection initialization

re-prioritize default connections
monero network status turns red on connection error
remove popup on connection error
remove popup on 0 monero peers which aren't reported
increase main wallet max sync attempts on startup
renames from btc to xmr
This commit is contained in:
woodser 2023-07-30 09:14:22 -04:00
parent 12b6585779
commit 473100620e
23 changed files with 283 additions and 325 deletions

View file

@ -36,7 +36,6 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Slf4j
@ -368,67 +367,6 @@ public final class CoreMoneroConnectionsService {
connectionManager.reset();
connectionManager.setTimeout(REFRESH_PERIOD_HTTP_MS);
// load previous or default connections
if (coreContext.isApiUser()) {
// load previous connections
for (MoneroRpcConnection connection : connectionList.getConnections()) connectionManager.addConnection(connection);
log.info("Read " + connectionList.getConnections().size() + " previous connections from disk");
// add default connections
for (XmrNode node : xmrNodes.getAllXmrNodes()) {
if (node.hasClearNetAddress()) {
MoneroRpcConnection connection = new MoneroRpcConnection(node.getAddress() + ":" + node.getPort()).setPriority(node.getPriority());
if (!connectionList.hasConnection(connection.getUri())) addConnection(connection);
}
if (node.hasOnionAddress()) {
MoneroRpcConnection connection = new MoneroRpcConnection(node.getOnionAddress() + ":" + node.getPort()).setPriority(node.getPriority());
if (!connectionList.hasConnection(connection.getUri())) addConnection(connection);
}
}
} else {
// add default connections
for (XmrNode node : xmrNodes.selectPreferredNodes(new XmrNodesSetupPreferences(preferences))) {
if (node.hasClearNetAddress()) {
MoneroRpcConnection connection = new MoneroRpcConnection(node.getAddress() + ":" + node.getPort()).setPriority(node.getPriority());
addConnection(connection);
}
if (node.hasOnionAddress()) {
MoneroRpcConnection connection = new MoneroRpcConnection(node.getOnionAddress() + ":" + node.getPort()).setPriority(node.getPriority());
addConnection(connection);
}
}
}
// set current connection
Optional<String> currentConnectionUri = Optional.empty();
if ("".equals(config.xmrNode)) {
if (coreContext.isApiUser() && connectionList.getCurrentConnectionUri().isPresent()) {
currentConnectionUri = connectionList.getCurrentConnectionUri();
connectionManager.setConnection(currentConnectionUri.get());
}
} else if (!isInitialized) {
// set monero connection from startup arguments
MoneroRpcConnection connection = new MoneroRpcConnection(config.xmrNode, config.xmrNodeUsername, config.xmrNodePassword).setPriority(1);
if (useProxy(connection)) connection.setProxyUri(getProxyUri());
connectionManager.setConnection(connection);
currentConnectionUri = Optional.of(connection.getUri());
}
// set connection proxies
log.info("TOR proxy URI: " + getProxyUri());
for (MoneroRpcConnection connection : connectionManager.getConnections()) {
if (useProxy(connection)) connection.setProxyUri(getProxyUri());
}
// restore configuration
if ("".equals(config.xmrNode)) connectionManager.setAutoSwitch(connectionList.getAutoSwitch());
// check connection
checkConnection();
// run once
if (!isInitialized) {
@ -439,6 +377,7 @@ public final class CoreMoneroConnectionsService {
log.info(getClass() + ".onNodeStarted() called");
daemon.getRpcConnection().checkConnection(connectionManager.getTimeout());
setConnection(daemon.getRpcConnection());
checkConnection();
}
@Override
@ -449,43 +388,88 @@ public final class CoreMoneroConnectionsService {
});
}
// if offline and last connection is local node, start local node if it's offline
currentConnectionUri.ifPresent(uri -> {
try {
if (!connectionManager.isConnected() && nodeService.equalsUri(uri) && !nodeService.isOnline()) {
// restore connections
if ("".equals(config.xmrNode)) {
// load previous or default connections
if (coreContext.isApiUser()) {
// load previous connections
for (MoneroRpcConnection connection : connectionList.getConnections()) connectionManager.addConnection(connection);
log.info("Read " + connectionList.getConnections().size() + " previous connections from disk");
// add default connections
for (XmrNode node : xmrNodes.getAllXmrNodes()) {
if (node.hasClearNetAddress()) {
MoneroRpcConnection connection = new MoneroRpcConnection(node.getAddress() + ":" + node.getPort()).setPriority(node.getPriority());
if (!connectionList.hasConnection(connection.getUri())) addConnection(connection);
}
if (node.hasOnionAddress()) {
MoneroRpcConnection connection = new MoneroRpcConnection(node.getOnionAddress() + ":" + node.getPort()).setPriority(node.getPriority());
if (!connectionList.hasConnection(connection.getUri())) addConnection(connection);
}
}
} else {
// add default connections
for (XmrNode node : xmrNodes.selectPreferredNodes(new XmrNodesSetupPreferences(preferences))) {
if (node.hasClearNetAddress()) {
MoneroRpcConnection connection = new MoneroRpcConnection(node.getAddress() + ":" + node.getPort()).setPriority(node.getPriority());
addConnection(connection);
}
if (node.hasOnionAddress()) {
MoneroRpcConnection connection = new MoneroRpcConnection(node.getOnionAddress() + ":" + node.getPort()).setPriority(node.getPriority());
addConnection(connection);
}
}
}
// restore last connection
if (connectionList.getCurrentConnectionUri().isPresent()) {
connectionManager.setConnection(connectionList.getCurrentConnectionUri().get());
}
// set connection proxies
log.info("TOR proxy URI: " + getProxyUri());
for (MoneroRpcConnection connection : connectionManager.getConnections()) {
if (useProxy(connection)) connection.setProxyUri(getProxyUri());
}
// restore auto switch
if (coreContext.isApiUser()) connectionManager.setAutoSwitch(connectionList.getAutoSwitch());
else connectionManager.setAutoSwitch(true);
// start local node if used as last connection and currently offline
if (connectionManager.getConnection() != null && nodeService.equalsUri(connectionManager.getConnection().getUri()) && !nodeService.isOnline()) {
try {
log.info("Starting local node");
nodeService.startMoneroNode();
} catch (Exception e) {
log.warn("Unable to start local monero node: " + e.getMessage());
e.printStackTrace();
}
} catch (Exception e) {
log.warn("Unable to start local monero node: " + e.getMessage());
e.printStackTrace();
}
});
// prefer to connect to local node unless prevented by configuration
if ("".equals(config.xmrNode) &&
(!connectionManager.isConnected() || connectionManager.getAutoSwitch()) &&
nodeService.isConnected()) {
MoneroRpcConnection connection = connectionManager.getConnectionByUri(nodeService.getDaemon().getRpcConnection().getUri());
if (connection != null) {
connection.checkConnection(connectionManager.getTimeout());
setConnection(connection);
// update connection
if (connectionManager.getConnection() == null || connectionManager.getAutoSwitch()) {
setConnection(getBestAvailableConnection());
} else {
checkConnection();
}
} else if (!isInitialized) {
// set connection from startup argument if given
connectionManager.setAutoSwitch(false);
MoneroRpcConnection connection = new MoneroRpcConnection(config.xmrNode, config.xmrNodeUsername, config.xmrNodePassword).setPriority(1);
if (useProxy(connection)) connection.setProxyUri(getProxyUri());
connectionManager.setConnection(connection);
}
// if using legacy desktop app, connect to best available connection
if (!coreContext.isApiUser() && "".equals(config.xmrNode)) {
connectionManager.setAutoSwitch(true);
MoneroRpcConnection bestConnection = connectionManager.getBestAvailableConnection();
log.info("Setting best available connection for monerod: " + (bestConnection == null ? null : bestConnection.getUri()));
connectionManager.setConnection(bestConnection);
}
// register connection change listener
// register connection listener
connectionManager.addListener(this::onConnectionChanged);
isInitialized = true;
// update connection state
// notify final connection
isInitialized = true;
onConnectionChanged(connectionManager.getConnection());
}
}
@ -556,10 +540,11 @@ public final class CoreMoneroConnectionsService {
numPeers.set(lastInfo.getNumOutgoingConnections() + lastInfo.getNumIncomingConnections());
peers.set(new ArrayList<MoneroPeer>());
// log recovery message
// handle error recovery
if (lastErrorTimestamp != null) {
log.info("Successfully fetched daemon info after previous error");
lastErrorTimestamp = null;
HavenoUtils.havenoSetup.getWalletServiceErrorMsg().set(null);
}
// update and notify connected state
@ -575,6 +560,9 @@ public final class CoreMoneroConnectionsService {
if (DevEnv.isDevMode()) e.printStackTrace();
}
// notify error message
HavenoUtils.havenoSetup.getWalletServiceErrorMsg().set(e.getMessage());
// check connection which notifies of changes
synchronized (this) {
if (connectionManager.getAutoSwitch()) connectionManager.setConnection(connectionManager.getBestAvailableConnection());

View file

@ -727,24 +727,24 @@ public class HavenoSetup {
///////////////////////////////////////////////////////////////////////////////////////////
// Wallet
public StringProperty getBtcInfo() {
return walletAppSetup.getBtcInfo();
public StringProperty getXmrInfo() {
return walletAppSetup.getXmrInfo();
}
public DoubleProperty getBtcSyncProgress() {
return walletAppSetup.getBtcSyncProgress();
public DoubleProperty getXmrSyncProgress() {
return walletAppSetup.getXmrSyncProgress();
}
public StringProperty getWalletServiceErrorMsg() {
return walletAppSetup.getWalletServiceErrorMsg();
}
public StringProperty getBtcSplashSyncIconId() {
return walletAppSetup.getBtcSplashSyncIconId();
public StringProperty getXmrSplashSyncIconId() {
return walletAppSetup.getXmrSplashSyncIconId();
}
public BooleanProperty getUseTorForBTC() {
return walletAppSetup.getUseTorForBTC();
public BooleanProperty getUseTorForXMR() {
return walletAppSetup.getUseTorForXMR();
}
// P2P

View file

@ -67,20 +67,20 @@ public class WalletAppSetup {
private final Preferences preferences;
@SuppressWarnings("FieldCanBeLocal")
private MonadicBinding<String> btcInfoBinding;
private MonadicBinding<String> xmrInfoBinding;
@Getter
private final DoubleProperty btcSyncProgress = new SimpleDoubleProperty(-1);
private final DoubleProperty xmrSyncProgress = new SimpleDoubleProperty(-1);
@Getter
private final StringProperty walletServiceErrorMsg = new SimpleStringProperty();
@Getter
private final StringProperty btcSplashSyncIconId = new SimpleStringProperty();
private final StringProperty xmrSplashSyncIconId = new SimpleStringProperty();
@Getter
private final StringProperty btcInfo = new SimpleStringProperty(Res.get("mainView.footer.btcInfo.initializing"));
private final StringProperty xmrInfo = new SimpleStringProperty(Res.get("mainView.footer.xmrInfo.initializing"));
@Getter
private final ObjectProperty<RejectedTxException> rejectedTxException = new SimpleObjectProperty<>();
@Getter
private final BooleanProperty useTorForBTC = new SimpleBooleanProperty();
private final BooleanProperty useTorForXMR = new SimpleBooleanProperty();
@Inject
public WalletAppSetup(CoreContext coreContext,
@ -95,7 +95,7 @@ public class WalletAppSetup {
this.connectionService = connectionService;
this.config = config;
this.preferences = preferences;
this.useTorForBTC.set(preferences.getUseTorForMonero());
this.useTorForXMR.set(preferences.getUseTorForMonero());
}
void init(@Nullable Consumer<String> chainFileLockedExceptionHandler,
@ -107,58 +107,59 @@ public class WalletAppSetup {
VersionMessage.BITCOINJ_VERSION, "2a80db4");
ObjectProperty<Throwable> walletServiceException = new SimpleObjectProperty<>();
btcInfoBinding = EasyBind.combine(connectionService.downloadPercentageProperty(), // TODO (woodser): update to XMR
xmrInfoBinding = EasyBind.combine(connectionService.downloadPercentageProperty(), // TODO (woodser): update to XMR
connectionService.chainHeightProperty(),
walletServiceException,
(downloadPercentage, chainHeight, exception) -> {
getWalletServiceErrorMsg(),
(downloadPercentage, chainHeight, exception, errorMsg) -> {
String result;
if (exception == null) {
if (exception == null && errorMsg == null) {
double percentage = (double) downloadPercentage;
btcSyncProgress.set(percentage);
xmrSyncProgress.set(percentage);
MoneroDaemonInfo lastInfo = connectionService.getLastInfo();
Long bestChainHeight = lastInfo == null ? null : lastInfo.getHeight();
String chainHeightAsString = bestChainHeight != null && bestChainHeight > 0 ?
String.valueOf(bestChainHeight) :
"";
if (percentage == 1) {
String synchronizedWith = Res.get("mainView.footer.btcInfo.synchronizedWith",
getBtcNetworkAsString(), chainHeightAsString);
String synchronizedWith = Res.get("mainView.footer.xmrInfo.synchronizedWith",
getXmrNetworkAsString(), chainHeightAsString);
String feeInfo = ""; // TODO: feeService.isFeeAvailable() returns true, disable
result = Res.get("mainView.footer.btcInfo", synchronizedWith, feeInfo);
getBtcSplashSyncIconId().set("image-connection-synced");
result = Res.get("mainView.footer.xmrInfo", synchronizedWith, feeInfo);
getXmrSplashSyncIconId().set("image-connection-synced");
downloadCompleteHandler.run();
} else if (percentage > 0.0) {
String synchronizingWith = Res.get("mainView.footer.btcInfo.synchronizingWith",
getBtcNetworkAsString(), chainHeightAsString,
String synchronizingWith = Res.get("mainView.footer.xmrInfo.synchronizingWith",
getXmrNetworkAsString(), chainHeightAsString,
FormattingUtils.formatToPercentWithSymbol(percentage));
result = Res.get("mainView.footer.btcInfo", synchronizingWith, "");
result = Res.get("mainView.footer.xmrInfo", synchronizingWith, "");
} else {
result = Res.get("mainView.footer.btcInfo",
Res.get("mainView.footer.btcInfo.connectingTo"),
getBtcNetworkAsString());
result = Res.get("mainView.footer.xmrInfo",
Res.get("mainView.footer.xmrInfo.connectingTo"),
getXmrNetworkAsString());
}
} else {
result = Res.get("mainView.footer.btcInfo",
Res.get("mainView.footer.btcInfo.connectionFailed"),
getBtcNetworkAsString());
log.error(exception.toString());
if (exception instanceof TimeoutException) {
getWalletServiceErrorMsg().set(Res.get("mainView.walletServiceErrorMsg.timeout"));
} else if (exception.getCause() instanceof BlockStoreException) {
if (exception.getCause().getCause() instanceof ChainFileLockedException && chainFileLockedExceptionHandler != null) {
chainFileLockedExceptionHandler.accept(Res.get("popup.warning.startupFailed.twoInstances"));
result = Res.get("mainView.footer.xmrInfo",
Res.get("mainView.footer.xmrInfo.connectionFailed"),
getXmrNetworkAsString());
if (exception != null) {
if (exception instanceof TimeoutException) {
getWalletServiceErrorMsg().set(Res.get("mainView.walletServiceErrorMsg.timeout"));
} else if (exception.getCause() instanceof BlockStoreException) {
if (exception.getCause().getCause() instanceof ChainFileLockedException && chainFileLockedExceptionHandler != null) {
chainFileLockedExceptionHandler.accept(Res.get("popup.warning.startupFailed.twoInstances"));
}
} else if (exception instanceof RejectedTxException) {
rejectedTxException.set((RejectedTxException) exception);
getWalletServiceErrorMsg().set(Res.get("mainView.walletServiceErrorMsg.rejectedTxException", exception.getMessage()));
} else {
getWalletServiceErrorMsg().set(Res.get("mainView.walletServiceErrorMsg.connectionError", exception.getMessage()));
}
} else if (exception instanceof RejectedTxException) {
rejectedTxException.set((RejectedTxException) exception);
getWalletServiceErrorMsg().set(Res.get("mainView.walletServiceErrorMsg.rejectedTxException", exception.getMessage()));
} else {
getWalletServiceErrorMsg().set(Res.get("mainView.walletServiceErrorMsg.connectionError", exception.getMessage()));
}
}
return result;
});
btcInfoBinding.subscribe((observable, oldValue, newValue) -> UserThread.execute(() -> btcInfo.set(newValue)));
xmrInfoBinding.subscribe((observable, oldValue, newValue) -> UserThread.execute(() -> xmrInfo.set(newValue)));
walletsSetup.initialize(null,
() -> {
@ -253,7 +254,7 @@ public class WalletAppSetup {
});
}
private String getBtcNetworkAsString() {
private String getXmrNetworkAsString() {
String postFix;
if (config.ignoreLocalXmrNode)
postFix = " " + Res.get("mainView.footer.localhostBitcoinNode");

View file

@ -56,22 +56,22 @@ public class XmrNodes {
return Arrays.asList(
new XmrNode(MoneroNodesOption.PROVIDED, null, null, "127.0.0.1", 38081, 1, "@local"),
new XmrNode(MoneroNodesOption.PROVIDED, null, null, "127.0.0.1", 39081, 1, "@local"),
new XmrNode(MoneroNodesOption.PROVIDED, null, null, "45.63.8.26", 38081, 1, "@haveno"),
new XmrNode(MoneroNodesOption.PROVIDED, null, null, "stagenet.community.rino.io", 38081, 2, "@RINOwallet"),
new XmrNode(MoneroNodesOption.PUBLIC, null, null, "stagenet.melo.tools", 38081, 2, null),
new XmrNode(MoneroNodesOption.PUBLIC, null, null, "node.sethforprivacy.com", 38089, 2, null),
new XmrNode(MoneroNodesOption.PUBLIC, null, null, "node2.sethforprivacy.com", 38089, 2, null),
new XmrNode(MoneroNodesOption.PROVIDED, null, null, "45.63.8.26", 38081, 2, "@haveno"),
new XmrNode(MoneroNodesOption.PROVIDED, null, null, "stagenet.community.rino.io", 38081, 3, "@RINOwallet"),
new XmrNode(MoneroNodesOption.PUBLIC, null, null, "stagenet.melo.tools", 38081, 3, null),
new XmrNode(MoneroNodesOption.PUBLIC, null, null, "node.sethforprivacy.com", 38089, 3, null),
new XmrNode(MoneroNodesOption.PUBLIC, null, null, "node2.sethforprivacy.com", 38089, 3, null),
new XmrNode(MoneroNodesOption.PUBLIC, null, "plowsof3t5hogddwabaeiyrno25efmzfxyro2vligremt7sxpsclfaid.onion", null, 38089, 2, null)
);
case XMR_MAINNET:
return Arrays.asList(
new XmrNode(MoneroNodesOption.PROVIDED, null, null, "127.0.0.1", 18081, 1, "@local"),
new XmrNode(MoneroNodesOption.PROVIDED, null, null, "xmr-node.cakewallet.com", 18081, 1, "@cakewallet"),
new XmrNode(MoneroNodesOption.PROVIDED, null, null, "xmr-node.cakewallet.com", 18081, 2, "@cakewallet"),
new XmrNode(MoneroNodesOption.PROVIDED, null, null, "xmr-node-eu.cakewallet.com", 18081, 2, "@cakewallet"),
new XmrNode(MoneroNodesOption.PROVIDED, null, null, "xmr-node-usa-east.cakewallet.com", 18081, 2, "@cakewallet"),
new XmrNode(MoneroNodesOption.PROVIDED, null, null, "xmr-node-uk.cakewallet.com", 18081, 2, "@cakewallet"),
new XmrNode(MoneroNodesOption.PUBLIC, null, null, "node.community.rino.io", 18081, 2, "@RINOwallet"),
new XmrNode(MoneroNodesOption.PUBLIC, null, null, "xmr-node-eu.cakewallet.com", 18081, 2, "@cakewallet"),
new XmrNode(MoneroNodesOption.PUBLIC, null, null, "xmr-node-usa-east.cakewallet.com", 18081, 2, "@cakewallet"),
new XmrNode(MoneroNodesOption.PUBLIC, null, null, "xmr-node-uk.cakewallet.com", 18081, 2, "@cakewallet"),
new XmrNode(MoneroNodesOption.PUBLIC, null, null, "node.sethforprivacy.com", 18089, 2, "@sethforprivacy")
new XmrNode(MoneroNodesOption.PUBLIC, null, null, "node.sethforprivacy.com", 18089, 3, "@sethforprivacy")
);
default:
throw new IllegalStateException("Unexpected base currency network: " + Config.baseCurrencyNetwork());

View file

@ -42,8 +42,8 @@ public class XmrNodesSetupPreferences {
XmrNodes.MoneroNodesOption nodesOption = XmrNodes.MoneroNodesOption.values()[preferences.getMoneroNodesOptionOrdinal()];
switch (nodesOption) {
case CUSTOM:
String bitcoinNodes = preferences.getMoneroNodes();
Set<String> distinctNodes = Utilities.commaSeparatedListToSet(bitcoinNodes, false);
String moneroNodes = preferences.getMoneroNodes();
Set<String> distinctNodes = Utilities.commaSeparatedListToSet(moneroNodes, false);
result = XmrNodes.toCustomXmrNodesList(distinctNodes);
if (result.isEmpty()) {
log.warn("Custom nodes is set but no valid nodes are provided. " +

View file

@ -675,7 +675,7 @@ public class XmrWalletService {
if (wallet != null) {
log.info("Monero wallet uri={}, path={}", wallet.getRpcConnection().getUri(), wallet.getPath());
int numAttempts = 0;
int maxAttempts = 2;
int maxAttempts = 3;
while (!HavenoUtils.havenoSetup.getWalletInitialized().get()) {
try {