support tor connection to monero network through monero-java

cleanup startup routine for stability
remove call to `get_connections`
increase wallet startup timeout to 1 hour
increase app startup timeout to 5 minutes
skip checkstyle in make commands
This commit is contained in:
woodser 2023-04-02 17:11:01 -04:00
parent 8305c62510
commit fd69f4250b
10 changed files with 149 additions and 88 deletions

View file

@ -16,11 +16,14 @@ haveno:
# build haveno without tests # build haveno without tests
skip-tests: localnet skip-tests: localnet
./gradlew build -x test ./gradlew build -x test -x checkstyleMain -x checkstyleTest
# quick build desktop and daemon apps without tests # quick build desktop and daemon apps without tests
haveno-apps: haveno-apps:
./gradlew :core:compileJava :desktop:build -x test ./gradlew :core:compileJava :desktop:build -x test -x checkstyleMain -x checkstyleTest
refresh-deps:
./gradlew --write-verification-metadata sha256 && ./gradlew build --refresh-keys --refresh-dependencies -x test -x checkstyleMain -x checkstyleTest
deploy: deploy:
# create a new screen session named 'localnet' # create a new screen session named 'localnet'
@ -84,6 +87,17 @@ monerod-local2:
--rpc-access-control-origins http://localhost:8080 \ --rpc-access-control-origins http://localhost:8080 \
--fixed-difficulty 300 --fixed-difficulty 300
funding-wallet-stagenet:
./.localnet/monero-wallet-rpc \
--rpc-bind-port 18084 \
--rpc-login rpc_user:abc123 \
--rpc-access-control-origins http://localhost:8080 \
--wallet-dir ./.localnet \
--daemon-ssl-allow-any-cert \
--daemon-address http://127.0.0.1:38081
#--proxy 127.0.0.1:49775 \
funding-wallet-local: funding-wallet-local:
./.localnet/monero-wallet-rpc \ ./.localnet/monero-wallet-rpc \
--testnet \ --testnet \
@ -214,10 +228,11 @@ arbitrator-daemon-stagenet:
--appName=haveno-XMR_STAGENET_arbitrator \ --appName=haveno-XMR_STAGENET_arbitrator \
--apiPassword=apitest \ --apiPassword=apitest \
--apiPort=9998 \ --apiPort=9998 \
--passwordRequired=false --passwordRequired=false \
--xmrNode=http://127.0.0.1:38081
# Arbitrator needs to be registered before making trades
arbitrator-desktop-stagenet: arbitrator-desktop-stagenet:
# Arbitrator needs to be registered before making trades
./haveno-desktop$(APP_EXT) \ ./haveno-desktop$(APP_EXT) \
--baseCurrencyNetwork=XMR_STAGENET \ --baseCurrencyNetwork=XMR_STAGENET \
--useLocalhostForP2P=false \ --useLocalhostForP2P=false \
@ -225,7 +240,8 @@ arbitrator-desktop-stagenet:
--nodePort=4444 \ --nodePort=4444 \
--appName=haveno-XMR_STAGENET_arbitrator \ --appName=haveno-XMR_STAGENET_arbitrator \
--apiPassword=apitest \ --apiPassword=apitest \
--apiPort=9998 --apiPort=9998 \
--xmrNode=http://127.0.0.1:38081
user1-daemon-stagenet: user1-daemon-stagenet:
./haveno-daemon$(APP_EXT) \ ./haveno-daemon$(APP_EXT) \

View file

@ -1,5 +1,6 @@
package haveno.core.api; package haveno.core.api;
import haveno.common.UserThread;
import haveno.common.app.DevEnv; import haveno.common.app.DevEnv;
import haveno.common.config.BaseCurrencyNetwork; import haveno.common.config.BaseCurrencyNetwork;
import haveno.common.config.Config; import haveno.common.config.Config;
@ -7,6 +8,7 @@ import haveno.core.trade.HavenoUtils;
import haveno.core.xmr.model.EncryptedConnectionList; import haveno.core.xmr.model.EncryptedConnectionList;
import haveno.core.xmr.setup.DownloadListener; import haveno.core.xmr.setup.DownloadListener;
import haveno.core.xmr.setup.WalletsSetup; import haveno.core.xmr.setup.WalletsSetup;
import haveno.network.Socks5ProxyProvider;
import javafx.beans.property.IntegerProperty; import javafx.beans.property.IntegerProperty;
import javafx.beans.property.LongProperty; import javafx.beans.property.LongProperty;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
@ -20,7 +22,6 @@ import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import monero.common.MoneroConnectionManager; import monero.common.MoneroConnectionManager;
import monero.common.MoneroConnectionManagerListener; import monero.common.MoneroConnectionManagerListener;
import monero.common.MoneroError;
import monero.common.MoneroRpcConnection; import monero.common.MoneroRpcConnection;
import monero.common.TaskLooper; import monero.common.TaskLooper;
import monero.daemon.MoneroDaemonRpc; import monero.daemon.MoneroDaemonRpc;
@ -34,6 +35,7 @@ import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Slf4j @Slf4j
@ -60,7 +62,7 @@ public final class CoreMoneroConnectionsService {
new MoneroRpcConnection("http://stagenet.melo.tools:38081").setPriority(2), new MoneroRpcConnection("http://stagenet.melo.tools:38081").setPriority(2),
new MoneroRpcConnection("http://node.sethforprivacy.com:38089").setPriority(2), new MoneroRpcConnection("http://node.sethforprivacy.com:38089").setPriority(2),
new MoneroRpcConnection("http://node2.sethforprivacy.com:38089").setPriority(2), new MoneroRpcConnection("http://node2.sethforprivacy.com:38089").setPriority(2),
new MoneroRpcConnection("http://ct36dsbe3oubpbebpxmiqz4uqk6zb6nhmkhoekileo4fts23rvuse2qd.onion:38081").setPriority(2) new MoneroRpcConnection("http://plowsof3t5hogddwabaeiyrno25efmzfxyro2vligremt7sxpsclfaid.onion:38089").setPriority(2)
)); ));
DEFAULT_CONNECTIONS.put(BaseCurrencyNetwork.XMR_MAINNET, Arrays.asList( DEFAULT_CONNECTIONS.put(BaseCurrencyNetwork.XMR_MAINNET, Arrays.asList(
new MoneroRpcConnection("http://127.0.0.1:18081").setPriority(1), new MoneroRpcConnection("http://127.0.0.1:18081").setPriority(1),
@ -84,6 +86,7 @@ public final class CoreMoneroConnectionsService {
private final IntegerProperty numPeers = new SimpleIntegerProperty(0); private final IntegerProperty numPeers = new SimpleIntegerProperty(0);
private final LongProperty chainHeight = new SimpleLongProperty(0); private final LongProperty chainHeight = new SimpleLongProperty(0);
private final DownloadListener downloadListener = new DownloadListener(); private final DownloadListener downloadListener = new DownloadListener();
private Socks5ProxyProvider socks5ProxyProvider;
private MoneroDaemonRpc daemon; private MoneroDaemonRpc daemon;
@Getter @Getter
@ -98,16 +101,15 @@ public final class CoreMoneroConnectionsService {
CoreAccountService accountService, CoreAccountService accountService,
CoreMoneroNodeService nodeService, CoreMoneroNodeService nodeService,
MoneroConnectionManager connectionManager, MoneroConnectionManager connectionManager,
EncryptedConnectionList connectionList) { EncryptedConnectionList connectionList,
Socks5ProxyProvider socks5ProxyProvider) {
this.config = config; this.config = config;
this.coreContext = coreContext; this.coreContext = coreContext;
this.accountService = accountService; this.accountService = accountService;
this.nodeService = nodeService; this.nodeService = nodeService;
this.connectionManager = connectionManager; this.connectionManager = connectionManager;
this.connectionList = connectionList; this.connectionList = connectionList;
this.socks5ProxyProvider = socks5ProxyProvider;
// initialize immediately if monerod configured
if (!"".equals(config.xmrNode)) initialize();
// initialize after account open and basic setup // initialize after account open and basic setup
walletsSetup.addSetupTaskHandler(() -> { // TODO: use something better than legacy WalletSetup for notification to initialize walletsSetup.addSetupTaskHandler(() -> { // TODO: use something better than legacy WalletSetup for notification to initialize
@ -145,6 +147,10 @@ public final class CoreMoneroConnectionsService {
return this.daemon; return this.daemon;
} }
public String getProxyUri() {
return socks5ProxyProvider.getSocks5Proxy() == null ? null : socks5ProxyProvider.getSocks5Proxy().getInetAddress().getHostAddress() + ":" + socks5ProxyProvider.getSocks5Proxy().getPort();
}
public void addListener(MoneroConnectionManagerListener listener) { public void addListener(MoneroConnectionManagerListener listener) {
synchronized (lock) { synchronized (lock) {
connectionManager.addListener(listener); connectionManager.addListener(listener);
@ -319,30 +325,41 @@ public final class CoreMoneroConnectionsService {
private void initialize() { private void initialize() {
synchronized (lock) { synchronized (lock) {
// reset connection manager's connections and listeners // reset connection manager
connectionManager.reset(); connectionManager.reset();
connectionManager.setTimeout(REFRESH_PERIOD_REMOTE_MS);
// load connections // load connections
connectionList.getConnections().forEach(connectionManager::addConnection); log.info("TOR proxy URI: " + getProxyUri());
for (MoneroRpcConnection connection : connectionList.getConnections()) {
if (connection.isOnion()) connection.setProxyUri(getProxyUri());
connectionManager.addConnection(connection);
}
log.info("Read " + connectionList.getConnections().size() + " connections from disk"); log.info("Read " + connectionList.getConnections().size() + " connections from disk");
// add default connections // add default connections
for (MoneroRpcConnection connection : DEFAULT_CONNECTIONS.get(Config.baseCurrencyNetwork())) { for (MoneroRpcConnection connection : DEFAULT_CONNECTIONS.get(Config.baseCurrencyNetwork())) {
if (connectionList.hasConnection(connection.getUri())) continue; if (connectionList.hasConnection(connection.getUri())) continue;
if (connection.isOnion()) connection.setProxyUri(getProxyUri());
addConnection(connection); addConnection(connection);
} }
// restore last used connection if present // restore last used connection if unconfigured and present
var currentConnectionUri = connectionList.getCurrentConnectionUri(); Optional<String> currentConnectionUri = null;
if ("".equals(config.xmrNode)) {
currentConnectionUri = connectionList.getCurrentConnectionUri();
if (currentConnectionUri.isPresent()) connectionManager.setConnection(currentConnectionUri.get()); if (currentConnectionUri.isPresent()) connectionManager.setConnection(currentConnectionUri.get());
} else if (!isInitialized) {
// set monero connection from startup arguments // set monero connection from startup arguments
if (!isInitialized && !"".equals(config.xmrNode)) { MoneroRpcConnection connection = new MoneroRpcConnection(config.xmrNode, config.xmrNodeUsername, config.xmrNodePassword).setPriority(1);
connectionManager.setConnection(new MoneroRpcConnection(config.xmrNode, config.xmrNodeUsername, config.xmrNodePassword).setPriority(1)); if (connection.isOnion()) connection.setProxyUri(getProxyUri());
connectionManager.setConnection(connection);
currentConnectionUri = Optional.of(connection.getUri());
} }
// restore configuration // restore configuration and check connection
connectionManager.setAutoSwitch(connectionList.getAutoSwitch()); if ("".equals(config.xmrNode)) connectionManager.setAutoSwitch(connectionList.getAutoSwitch());
long refreshPeriod = connectionList.getRefreshPeriod(); long refreshPeriod = connectionList.getRefreshPeriod();
if (refreshPeriod > 0) connectionManager.startCheckingConnection(refreshPeriod); if (refreshPeriod > 0) connectionManager.startCheckingConnection(refreshPeriod);
else if (refreshPeriod == 0) connectionManager.startCheckingConnection(); else if (refreshPeriod == 0) connectionManager.startCheckingConnection();
@ -351,9 +368,6 @@ public final class CoreMoneroConnectionsService {
// run once // run once
if (!isInitialized) { if (!isInitialized) {
// register connection change listener
connectionManager.addListener(this::onConnectionChanged);
// register local node listener // register local node listener
nodeService.addListener(new MoneroNodeServiceListener() { nodeService.addListener(new MoneroNodeServiceListener() {
@Override @Override
@ -369,8 +383,6 @@ public final class CoreMoneroConnectionsService {
checkConnection(); checkConnection();
} }
}); });
isInitialized = true;
} }
// if offline and last connection is local, start local node if offline // if offline and last connection is local, start local node if offline
@ -385,7 +397,7 @@ public final class CoreMoneroConnectionsService {
}); });
// prefer to connect to local node unless prevented by configuration // prefer to connect to local node unless prevented by configuration
if (("".equals(config.xmrNode) || HavenoUtils.isLocalHost(config.xmrNode)) && if ("".equals(config.xmrNode) &&
(!connectionManager.isConnected() || connectionManager.getAutoSwitch()) && (!connectionManager.isConnected() || connectionManager.getAutoSwitch()) &&
nodeService.isConnected()) { nodeService.isConnected()) {
MoneroRpcConnection connection = connectionManager.getConnectionByUri(nodeService.getDaemon().getRpcConnection().getUri()); MoneroRpcConnection connection = connectionManager.getConnectionByUri(nodeService.getDaemon().getRpcConnection().getUri());
@ -401,12 +413,19 @@ public final class CoreMoneroConnectionsService {
connectionManager.setConnection(connectionManager.getBestAvailableConnection()); connectionManager.setConnection(connectionManager.getBestAvailableConnection());
} }
// update connection // register connection change listener
if (!isInitialized) {
connectionManager.addListener(this::onConnectionChanged);
isInitialized = true;
}
// announce connection
onConnectionChanged(connectionManager.getConnection()); onConnectionChanged(connectionManager.getConnection());
} }
} }
private void onConnectionChanged(MoneroRpcConnection currentConnection) { private void onConnectionChanged(MoneroRpcConnection currentConnection) {
// TODO: ignore if shutdown
synchronized (lock) { synchronized (lock) {
if (currentConnection == null) { if (currentConnection == null) {
daemon = null; daemon = null;
@ -422,13 +441,18 @@ public final class CoreMoneroConnectionsService {
} }
private void startPollingDaemon() { private void startPollingDaemon() {
synchronized (lock) {
updateDaemonInfo();
if (updateDaemonLooper != null) updateDaemonLooper.stop(); if (updateDaemonLooper != null) updateDaemonLooper.stop();
updateDaemonInfo(); UserThread.runAfter(() -> {
updateDaemonLooper = new TaskLooper(() -> { synchronized (lock) {
updateDaemonInfo(); if (updateDaemonLooper != null) updateDaemonLooper.stop();
}); updateDaemonLooper = new TaskLooper(() -> updateDaemonInfo());
updateDaemonLooper.start(getDefaultRefreshPeriodMs()); updateDaemonLooper.start(getDefaultRefreshPeriodMs());
} }
}, getDefaultRefreshPeriodMs() / 1000);
}
}
private void updateDaemonInfo() { private void updateDaemonInfo() {
try { try {
@ -438,12 +462,18 @@ public final class CoreMoneroConnectionsService {
//System.out.println(JsonUtils.serialize(lastInfo)); //System.out.println(JsonUtils.serialize(lastInfo));
//System.out.println(JsonUtils.serialize(daemon.getSyncInfo())); //System.out.println(JsonUtils.serialize(daemon.getSyncInfo()));
chainHeight.set(lastInfo.getTargetHeight() == 0 ? lastInfo.getHeight() : lastInfo.getTargetHeight()); chainHeight.set(lastInfo.getTargetHeight() == 0 ? lastInfo.getHeight() : lastInfo.getTargetHeight());
try {
peers.set(getOnlinePeers()); // set peer connections
} catch (MoneroError err) { // TODO: peers often uknown due to restricted RPC call, skipping call to get peer connections
peers.set(new ArrayList<MoneroPeer>()); // TODO: peers unknown due to restricted RPC call // try {
} // peers.set(getOnlinePeers());
numPeers.set(peers.get().size()); // } catch (Exception err) {
// // TODO: peers unknown due to restricted RPC call
// }
// numPeers.set(peers.get().size());
numPeers.set(lastInfo.getNumOutgoingConnections() + lastInfo.getNumIncomingConnections());
peers.set(new ArrayList<MoneroPeer>());
if (lastErrorTimestamp != null) { if (lastErrorTimestamp != null) {
log.info("Successfully fetched daemon info after previous error"); log.info("Successfully fetched daemon info after previous error");
lastErrorTimestamp = null; lastErrorTimestamp = null;

View file

@ -17,11 +17,9 @@
package haveno.core.app; package haveno.core.app;
import ch.qos.logback.classic.Level;
import haveno.common.Timer; import haveno.common.Timer;
import haveno.common.UserThread; import haveno.common.UserThread;
import haveno.common.app.DevEnv; import haveno.common.app.DevEnv;
import haveno.common.app.Log;
import haveno.common.app.Version; import haveno.common.app.Version;
import haveno.common.config.BaseCurrencyNetwork; import haveno.common.config.BaseCurrencyNetwork;
import haveno.common.config.Config; import haveno.common.config.Config;
@ -102,7 +100,7 @@ public class HavenoSetup {
private static final String VERSION_FILE_NAME = "version"; private static final String VERSION_FILE_NAME = "version";
private static final String RESYNC_SPV_FILE_NAME = "resyncSpv"; private static final String RESYNC_SPV_FILE_NAME = "resyncSpv";
private static final long STARTUP_TIMEOUT_MINUTES = 4; private static final long STARTUP_TIMEOUT_MINUTES = 5;
private final DomainInitialisation domainInitialisation; private final DomainInitialisation domainInitialisation;
private final P2PNetworkSetup p2PNetworkSetup; private final P2PNetworkSetup p2PNetworkSetup;
@ -403,9 +401,9 @@ public class HavenoSetup {
if (displayTorNetworkSettingsHandler != null) if (displayTorNetworkSettingsHandler != null)
displayTorNetworkSettingsHandler.accept(true); displayTorNetworkSettingsHandler.accept(true);
log.info("Set log level for org.berndpruenster.netlayer classes to DEBUG to show more details for " + // log.info("Set log level for org.berndpruenster.netlayer classes to DEBUG to show more details for " +
"Tor network connection issues"); // "Tor network connection issues");
Log.setCustomLogLevel("org.berndpruenster.netlayer", Level.DEBUG); // Log.setCustomLogLevel("org.berndpruenster.netlayer", Level.DEBUG);
}, STARTUP_TIMEOUT_MINUTES, TimeUnit.MINUTES); }, STARTUP_TIMEOUT_MINUTES, TimeUnit.MINUTES);
@ -444,7 +442,7 @@ public class HavenoSetup {
checkForInvalidMakerFeeTxs(); checkForInvalidMakerFeeTxs();
} }
}, },
() -> walletInitialized.set(true)); () -> {});
} }
private void initDomainServices() { private void initDomainServices() {

View file

@ -142,12 +142,12 @@ public class P2PNetworkSetup {
bootstrapState.set(Res.get("mainView.bootstrapState.torNodeCreated")); bootstrapState.set(Res.get("mainView.bootstrapState.torNodeCreated"));
p2PNetworkIconId.set("image-connection-tor"); p2PNetworkIconId.set("image-connection-tor");
// invoke handler to initialize wallet
initWalletServiceHandler.run();
// We want to get early connected to the price relay so we call it already now // We want to get early connected to the price relay so we call it already now
priceFeedService.setCurrencyCodeOnInit(); priceFeedService.setCurrencyCodeOnInit();
priceFeedService.requestPrices(); priceFeedService.requestPrices();
// invoke handler to initialize wallet
initWalletServiceHandler.run();
} }
@Override @Override

View file

@ -424,7 +424,7 @@ public class HavenoUtils {
public static boolean isLocalHost(String uri) { public static boolean isLocalHost(String uri) {
try { try {
String host = new URI(uri).getHost(); String host = new URI(uri).getHost();
return host.equals(LOOPBACK_HOST) || host.equals(LOCALHOST); return LOOPBACK_HOST.equals(host) || LOCALHOST.equals(host);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View file

@ -1662,13 +1662,17 @@ public abstract class Trade implements Tradable, Model {
private void setWalletRefreshPeriod(long walletRefreshPeriod) { private void setWalletRefreshPeriod(long walletRefreshPeriod) {
if (this.isShutDown) return; if (this.isShutDown) return;
if (this.walletRefreshPeriod != null && this.walletRefreshPeriod == walletRefreshPeriod) return; if (this.walletRefreshPeriod != null && this.walletRefreshPeriod == walletRefreshPeriod) return;
log.info("Setting wallet refresh rate for {} {} to {}", getClass().getSimpleName(), getId(), walletRefreshPeriod);
this.walletRefreshPeriod = walletRefreshPeriod; this.walletRefreshPeriod = walletRefreshPeriod;
synchronized (walletLock) {
if (getWallet() != null) {
log.info("Setting wallet refresh rate for {} {} to {}", getClass().getSimpleName(), getId(), walletRefreshPeriod);
getWallet().startSyncing(getWalletRefreshPeriod()); // TODO (monero-project): wallet rpc waits until last sync period finishes before starting new sync period getWallet().startSyncing(getWalletRefreshPeriod()); // TODO (monero-project): wallet rpc waits until last sync period finishes before starting new sync period
}
if (txPollLooper != null) { if (txPollLooper != null) {
txPollLooper.stop(); txPollLooper.stop();
txPollLooper = null; txPollLooper = null;
} }
}
startPolling(); startPolling();
} }

View file

@ -349,7 +349,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
.collect(Collectors.toSet()); .collect(Collectors.toSet());
unreservedFrozenKeyImages.removeAll(reservedKeyImages); unreservedFrozenKeyImages.removeAll(reservedKeyImages);
if (!unreservedFrozenKeyImages.isEmpty()) { if (!unreservedFrozenKeyImages.isEmpty()) {
log.info("Thawing outputs which are not reserved for offer or trade: " + unreservedFrozenKeyImages); log.warn("Thawing outputs which are not reserved for offer or trade: " + unreservedFrozenKeyImages);
xmrWalletService.thawOutputs(unreservedFrozenKeyImages); xmrWalletService.thawOutputs(unreservedFrozenKeyImages);
xmrWalletService.saveMainWallet(); xmrWalletService.saveMainWallet();
} }

View file

@ -92,7 +92,7 @@ public class WalletsSetup {
@Getter @Getter
public final BooleanProperty walletsSetupFailed = new SimpleBooleanProperty(); public final BooleanProperty walletsSetupFailed = new SimpleBooleanProperty();
private static final long STARTUP_TIMEOUT = 180; private static final long STARTUP_TIMEOUT_SECONDS = 3600; // 1 hour
private static final String SPV_CHAIN_FILE_NAME = "haveno.spvchain"; private static final String SPV_CHAIN_FILE_NAME = "haveno.spvchain";
private final RegTestHost regTestHost; private final RegTestHost regTestHost;
@ -167,7 +167,7 @@ public class WalletsSetup {
Timer timeoutTimer = UserThread.runAfter(() -> Timer timeoutTimer = UserThread.runAfter(() ->
exceptionHandler.handleException(new TimeoutException("Wallet did not initialize in " + exceptionHandler.handleException(new TimeoutException("Wallet did not initialize in " +
STARTUP_TIMEOUT + " seconds.")), STARTUP_TIMEOUT); STARTUP_TIMEOUT_SECONDS + " seconds.")), STARTUP_TIMEOUT_SECONDS);
backupWallets(); backupWallets();

View file

@ -211,7 +211,7 @@ public class XmrWalletService {
public MoneroWalletRpc createWallet(String walletName) { public MoneroWalletRpc createWallet(String walletName) {
log.info("{}.createWallet({})", getClass().getSimpleName(), walletName); log.info("{}.createWallet({})", getClass().getSimpleName(), walletName);
if (isShutDown) throw new IllegalStateException("Cannot create wallet because shutting down"); if (isShutDown) throw new IllegalStateException("Cannot create wallet because shutting down");
return createWallet(new MoneroWalletConfig() return createWalletRpc(new MoneroWalletConfig()
.setPath(walletName) .setPath(walletName)
.setPassword(getWalletPassword()), .setPassword(getWalletPassword()),
null); null);
@ -220,7 +220,7 @@ public class XmrWalletService {
public MoneroWalletRpc openWallet(String walletName) { public MoneroWalletRpc openWallet(String walletName) {
log.info("{}.openWallet({})", getClass().getSimpleName(), walletName); log.info("{}.openWallet({})", getClass().getSimpleName(), walletName);
if (isShutDown) throw new IllegalStateException("Cannot open wallet because shutting down"); if (isShutDown) throw new IllegalStateException("Cannot open wallet because shutting down");
return openWallet(new MoneroWalletConfig() return openWalletRpc(new MoneroWalletConfig()
.setPath(walletName) .setPath(walletName)
.setPassword(getWalletPassword()), .setPassword(getWalletPassword()),
null); null);
@ -546,51 +546,49 @@ public class XmrWalletService {
private void maybeInitMainWallet() { private void maybeInitMainWallet() {
if (wallet != null) throw new RuntimeException("Main wallet is already initialized"); if (wallet != null) throw new RuntimeException("Main wallet is already initialized");
MoneroDaemonRpc daemon = connectionsService.getDaemon();
log.info("Initializing main wallet with " + (daemon == null ? "daemon: null" : "monerod uri=" + daemon.getRpcConnection().getUri() + ", height=" + connectionsService.getLastInfo().getHeight()));
// open or create wallet // open or create wallet
MoneroWalletConfig walletConfig = new MoneroWalletConfig().setPath(MONERO_WALLET_NAME).setPassword(getWalletPassword()); MoneroWalletConfig walletConfig = new MoneroWalletConfig().setPath(MONERO_WALLET_NAME).setPassword(getWalletPassword());
if (MoneroUtils.walletExists(xmrWalletFile.getPath())) { if (MoneroUtils.walletExists(xmrWalletFile.getPath())) {
wallet = openWallet(walletConfig, rpcBindPort); wallet = openWalletRpc(walletConfig, rpcBindPort);
} else if (connectionsService.getConnection() != null && Boolean.TRUE.equals(connectionsService.getConnection().isConnected())) { } else if (connectionsService.getConnection() != null && Boolean.TRUE.equals(connectionsService.getConnection().isConnected())) {
wallet = createWallet(walletConfig, rpcBindPort); wallet = createWalletRpc(walletConfig, rpcBindPort);
} }
// wallet is not initialized until connected to a daemon // handle when wallet initialized and synced
if (wallet != null) { if (wallet != null) {
if (connectionsService.getDaemon() == null) System.out.println("Daemon: null"); log.info("Monero wallet uri={}, path={}", wallet.getRpcConnection().getUri(), wallet.getPath());
else {
System.out.println("Daemon uri: " + connectionsService.getDaemon().getRpcConnection().getUri());
System.out.println("Daemon height: " + connectionsService.getDaemon().getInfo().getHeight());
}
System.out.println("Monero wallet uri: " + wallet.getRpcConnection().getUri());
System.out.println("Monero wallet path: " + wallet.getPath());
System.out.println("Monero wallet primary address: " + wallet.getPrimaryAddress());
// sync wallet which updates app startup state
try { try {
// sync main wallet
log.info("Syncing main wallet"); log.info("Syncing main wallet");
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
wallet.sync(); // blocking wallet.sync(); // blocking
log.info("Done syncing main wallet in " + (System.currentTimeMillis() - time) + " ms"); log.info("Done syncing main wallet in " + (System.currentTimeMillis() - time) + " ms");
wallet.startSyncing(connectionsService.getDefaultRefreshPeriodMs()); wallet.startSyncing(connectionsService.getDefaultRefreshPeriodMs());
connectionsService.doneDownload(); // TODO: using this to signify both daemon and wallet synced, refactor sync handling of both if (getMoneroNetworkType() != MoneroNetworkType.MAINNET) log.info("Monero wallet balance={}, unlocked balance={}", wallet.getBalance(0), wallet.getUnlockedBalance(0));
saveMainWallet(false); // skip backup on open
// TODO: using this to signify both daemon and wallet synced, refactor sync handling of both
connectionsService.doneDownload();
// save but skip backup on initialization
saveMainWallet(false);
} catch (Exception e) { } catch (Exception e) {
log.warn("Error syncing main wallet: {}", e.getMessage()); log.warn("Error syncing main wallet: {}", e.getMessage());
} }
System.out.println("Monero wallet balance: " + wallet.getBalance(0));
System.out.println("Monero wallet unlocked balance: " + wallet.getUnlockedBalance(0));
// notify setup that main wallet is initialized // notify setup that main wallet is initialized
havenoSetup.getWalletInitialized().set(true); // TODO: change to listener pattern? // TODO: move to try..catch? refactor startup to call this and sync off main thread?
havenoSetup.getWalletInitialized().set(true); // TODO: change to listener pattern
// register internal listener to notify external listeners // register internal listener to notify external listeners
wallet.addListener(new XmrWalletListener()); wallet.addListener(new XmrWalletListener());
} }
} }
private MoneroWalletRpc createWallet(MoneroWalletConfig config, Integer port) { private MoneroWalletRpc createWalletRpc(MoneroWalletConfig config, Integer port) {
// must be connected to daemon // must be connected to daemon
MoneroRpcConnection connection = connectionsService.getConnection(); MoneroRpcConnection connection = connectionsService.getConnection();
@ -602,10 +600,15 @@ public class XmrWalletService {
// create wallet // create wallet
try { try {
log.info("Creating wallet " + config.getPath());
// prevent wallet rpc from syncing
walletRpc.stopSyncing();
// create wallet
log.info("Creating wallet " + config.getPath() + " connected to daemon " + connection.getUri());
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
walletRpc.createWallet(config); walletRpc.createWallet(config);
log.info("Done creating wallet " + walletRpc.getPath() + " in " + (System.currentTimeMillis() - time) + " ms"); log.info("Done creating wallet " + config.getPath() + " in " + (System.currentTimeMillis() - time) + " ms");
return walletRpc; return walletRpc;
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -614,17 +617,21 @@ public class XmrWalletService {
} }
} }
private MoneroWalletRpc openWallet(MoneroWalletConfig config, Integer port) { private MoneroWalletRpc openWalletRpc(MoneroWalletConfig config, Integer port) {
// start monero-wallet-rpc instance // start monero-wallet-rpc instance
MoneroWalletRpc walletRpc = startWalletRpcInstance(port); MoneroWalletRpc walletRpc = startWalletRpcInstance(port);
// open wallet // open wallet
try { try {
// prevent wallet rpc from syncing
walletRpc.stopSyncing();
// open wallet
log.info("Opening wallet " + config.getPath()); log.info("Opening wallet " + config.getPath());
walletRpc.openWallet(config); walletRpc.openWallet(config.setServer(connectionsService.getConnection()));
walletRpc.setDaemonConnection(connectionsService.getConnection()); log.info("Done opening wallet " + config.getPath());
log.info("Done opening wallet " + walletRpc.getPath());
return walletRpc; return walletRpc;
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -655,6 +662,10 @@ public class XmrWalletService {
if (connection != null) { if (connection != null) {
cmd.add("--daemon-address"); cmd.add("--daemon-address");
cmd.add(connection.getUri()); cmd.add(connection.getUri());
if (connection.isOnion() && connection.getProxyUri() != null) {
cmd.add("--proxy");
cmd.add(connection.getProxyUri());
}
if (connection.getUsername() != null) { if (connection.getUsername() != null) {
cmd.add("--daemon-login"); cmd.add("--daemon-login");
cmd.add(connection.getUsername() + ":" + connection.getPassword()); cmd.add(connection.getUsername() + ":" + connection.getPassword());
@ -669,7 +680,9 @@ public class XmrWalletService {
return MONERO_WALLET_RPC_MANAGER.startInstance(cmd); return MONERO_WALLET_RPC_MANAGER.startInstance(cmd);
} }
// TODO: monero-wallet-rpc needs restarted if applying tor proxy
private void setDaemonConnection(MoneroRpcConnection connection) { private void setDaemonConnection(MoneroRpcConnection connection) {
if (isShutDown) return;
log.info("Setting wallet daemon connection: " + (connection == null ? null : connection.getUri())); log.info("Setting wallet daemon connection: " + (connection == null ? null : connection.getUri()));
if (wallet == null) maybeInitMainWallet(); if (wallet == null) maybeInitMainWallet();
else { else {

View file

@ -117,7 +117,7 @@ public class HavenoApp extends Application implements UncaughtExceptionHandler {
} }
public void startApplication(Runnable onApplicationStartedHandler) { public void startApplication(Runnable onApplicationStartedHandler) {
log.info("Running startApplication..."); log.info("Starting application");
try { try {
mainView = loadMainView(injector); mainView = loadMainView(injector);
mainView.setOnApplicationStartedHandler(onApplicationStartedHandler); mainView.setOnApplicationStartedHandler(onApplicationStartedHandler);