app fully initialized before daemon connection or wallet by default

wallet initializes when first connected to get correct height
connect to local node if available and last connection offline
use only one internal daemon in monero node service
This commit is contained in:
woodser 2022-04-02 14:18:11 -04:00
parent 9dfbb0d5a6
commit fdddc87477
6 changed files with 176 additions and 140 deletions

View File

@ -243,7 +243,7 @@ public final class CoreMoneroConnectionsService {
/** /**
* Signals that both the daemon and wallet have synced. * Signals that both the daemon and wallet have synced.
* *
* TODO: separate daemon and wallet download/done listeners * TODO: separate daemon and wallet download/done listeners
*/ */
public void doneDownload() { public void doneDownload() {
@ -268,15 +268,9 @@ public final class CoreMoneroConnectionsService {
addConnection(connection); addConnection(connection);
} }
// restore last used connection // restore last used connection if present
var currentConnection = connectionList.getCurrentConnectionUri(); var currentConnectionUri = connectionList.getCurrentConnectionUri();
currentConnection.ifPresentOrElse(connectionManager::setConnection, () -> { if (currentConnectionUri.isPresent()) connectionManager.setConnection(currentConnectionUri.get());
connectionManager.setConnection(DEFAULT_CONNECTIONS.get(0).getUri()); // default to localhost
});
// initialize daemon
daemon = new MoneroDaemonRpc(connectionManager.getConnection());
updateDaemonInfo();
// restore configuration // restore configuration
connectionManager.setAutoSwitch(connectionList.getAutoSwitch()); connectionManager.setAutoSwitch(connectionList.getAutoSwitch());
@ -288,11 +282,15 @@ public final class CoreMoneroConnectionsService {
// run once // run once
if (!isInitialized) { if (!isInitialized) {
// initialize local monero node // register connection change listener
connectionManager.addListener(this::onConnectionChanged);
// register local node listener
nodeService.addListener(new MoneroNodeServiceListener() { nodeService.addListener(new MoneroNodeServiceListener() {
@Override @Override
public void onNodeStarted(MoneroDaemonRpc daemon) { public void onNodeStarted(MoneroDaemonRpc daemon) {
log.info(getClass() + ".onNodeStarted() called"); log.info(getClass() + ".onNodeStarted() called");
daemon.getRpcConnection().checkConnection(connectionManager.getTimeout());
setConnection(daemon.getRpcConnection()); setConnection(daemon.getRpcConnection());
} }
@ -303,10 +301,10 @@ public final class CoreMoneroConnectionsService {
} }
}); });
// start local node if the last connection is local and not running // start local node if last connection is local and offline
currentConnection.ifPresent(connection -> { currentConnectionUri.ifPresent(uri -> {
try { try {
if (nodeService.isMoneroNodeConnection(connection) && !nodeService.isMoneroNodeRunning()) { if (CoreMoneroNodeService.isLocalHost(uri) && !nodeService.isMoneroNodeRunning()) {
nodeService.startMoneroNode(); nodeService.startMoneroNode();
} }
} catch (Exception e) { } catch (Exception e) {
@ -314,13 +312,22 @@ public final class CoreMoneroConnectionsService {
} }
}); });
// register connection change listener
connectionManager.addListener(this::onConnectionChanged);
// poll daemon periodically // poll daemon periodically
startPollingDaemon(); startPollingDaemon();
isInitialized = true; isInitialized = true;
} }
// if offline, connect to local node if available
if (!connectionManager.isConnected() && nodeService.isMoneroNodeRunning()) {
MoneroRpcConnection connection = connectionManager.getConnectionByUri(nodeService.getDaemon().getRpcConnection().getUri());
if (connection == null) connection = nodeService.getDaemon().getRpcConnection();
connection.checkConnection(connectionManager.getTimeout());
setConnection(connection);
}
// set the daemon based on the connection
if (getConnection() != null) daemon = new MoneroDaemonRpc(connectionManager.getConnection());
updateDaemonInfo();
} }
} }

View File

@ -36,17 +36,17 @@ import java.util.List;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import monero.common.MoneroRpcConnection;
import monero.daemon.MoneroDaemonRpc; import monero.daemon.MoneroDaemonRpc;
/** /**
* Manages a Monero node instance or connection to an instance. * Start and stop or connect to a local Monero node.
*/ */
@Slf4j @Slf4j
@Singleton @Singleton
public class CoreMoneroNodeService { public class CoreMoneroNodeService {
public static final String LOCAL_NODE_ADDRESS = "127.0.0.1"; // expected connection from local MoneroDaemonRpc private static final String LOOPBACK_HOST = "127.0.0.1"; // local loopback address to host Monero node
private static final String LOCALHOST = "localhost";
private static final String MONERO_NETWORK_TYPE = Config.baseCurrencyNetwork().getNetwork().toLowerCase(); private static final String MONERO_NETWORK_TYPE = Config.baseCurrencyNetwork().getNetwork().toLowerCase();
private static final String MONEROD_PATH = System.getProperty("user.dir") + File.separator + ".localnet" + File.separator + "monerod"; private static final String MONEROD_PATH = System.getProperty("user.dir") + File.separator + ".localnet" + File.separator + "monerod";
private static final String MONEROD_DATADIR = System.getProperty("user.dir") + File.separator + ".localnet" + File.separator + MONERO_NETWORK_TYPE; private static final String MONEROD_DATADIR = System.getProperty("user.dir") + File.separator + ".localnet" + File.separator + MONERO_NETWORK_TYPE;
@ -63,15 +63,11 @@ public class CoreMoneroNodeService {
"--rpc-login", "superuser:abctesting123" // TODO: remove authentication "--rpc-login", "superuser:abctesting123" // TODO: remove authentication
); );
// local monero node owned by this process // client to the local Monero node
private MoneroDaemonRpc daemon; private MoneroDaemonRpc daemon;
// local monero node for detecting running node not owned by this process
private MoneroDaemonRpc defaultMoneroDaemon;
@Inject @Inject
public CoreMoneroNodeService(Preferences preferences) { public CoreMoneroNodeService(Preferences preferences) {
this.daemon = null;
this.preferences = preferences; this.preferences = preferences;
int rpcPort = 18081; // mainnet int rpcPort = 18081; // mainnet
if (Config.baseCurrencyNetwork().isTestnet()) { if (Config.baseCurrencyNetwork().isTestnet()) {
@ -79,9 +75,15 @@ public class CoreMoneroNodeService {
} else if (Config.baseCurrencyNetwork().isStagenet()) { } else if (Config.baseCurrencyNetwork().isStagenet()) {
rpcPort = 38081; rpcPort = 38081;
} }
// TODO: remove authentication this.daemon = new MoneroDaemonRpc("http://" + LOOPBACK_HOST + ":" + rpcPort, "superuser", "abctesting123"); // TODO: remove authentication
var defaultMoneroConnection = new MoneroRpcConnection("http://" + LOCAL_NODE_ADDRESS + ":" + rpcPort, "superuser", "abctesting123").setPriority(1); // localhost is first priority }
defaultMoneroDaemon = new MoneroDaemonRpc(defaultMoneroConnection);
/**
* Returns whether the given URI is on local host. // TODO: move to utils
*/
public static boolean isLocalHost(String uri) throws URISyntaxException {
String host = new URI(uri).getHost();
return host.equals(CoreMoneroNodeService.LOOPBACK_HOST) || host.equals(CoreMoneroNodeService.LOCALHOST);
} }
public void addListener(MoneroNodeServiceListener listener) { public void addListener(MoneroNodeServiceListener listener) {
@ -93,18 +95,17 @@ public class CoreMoneroNodeService {
} }
/** /**
* Returns whether a connection string URI is a local monero node. * Returns the client of the local monero node.
*/ */
public boolean isMoneroNodeConnection(String connection) throws URISyntaxException { public MoneroDaemonRpc getDaemon() {
var uri = new URI(connection); return daemon;
return CoreMoneroNodeService.LOCAL_NODE_ADDRESS.equals(uri.getHost());
} }
/** /**
* Returns whether the local monero node is running or local daemon connection is running * Returns whether a local monero node is running.
*/ */
public boolean isMoneroNodeRunning() { public boolean isMoneroNodeRunning() {
return daemon != null || defaultMoneroDaemon.isConnected(); return daemon.isConnected();
} }
public MoneroNodeSettings getMoneroNodeSettings() { public MoneroNodeSettings getMoneroNodeSettings() {
@ -124,7 +125,7 @@ public class CoreMoneroNodeService {
* Persists the settings to preferences if the node started successfully. * Persists the settings to preferences if the node started successfully.
*/ */
public void startMoneroNode(MoneroNodeSettings settings) throws IOException { public void startMoneroNode(MoneroNodeSettings settings) throws IOException {
if (isMoneroNodeRunning()) throw new IllegalStateException("Monero node already running"); if (isMoneroNodeRunning()) throw new IllegalStateException("Local Monero node already running");
log.info("Starting local Monero node: " + settings); log.info("Starting local Monero node: " + settings);
@ -146,23 +147,19 @@ public class CoreMoneroNodeService {
args.addAll(flags); args.addAll(flags);
} }
daemon = new MoneroDaemonRpc(args); daemon = new MoneroDaemonRpc(args); // start daemon as process and re-assign client
preferences.setMoneroNodeSettings(settings); preferences.setMoneroNodeSettings(settings);
for (var listener : listeners) listener.onNodeStarted(daemon); for (var listener : listeners) listener.onNodeStarted(daemon);
} }
/** /**
* Stops the current local monero node if owned by this process. * Stops the current local monero node if we own its process.
* Does not remove the last MoneroNodeSettings. * Does not remove the last MoneroNodeSettings.
*/ */
public void stopMoneroNode() { public void stopMoneroNode() {
if (!isMoneroNodeRunning()) throw new IllegalStateException("Monero node is not running"); if (!isMoneroNodeRunning()) throw new IllegalStateException("Local Monero node is not running");
if (daemon != null) { if (daemon.getProcess() == null || !daemon.getProcess().isAlive()) throw new IllegalStateException("Cannot stop local Monero node because we don't own its process"); // TODO (woodser): remove isAlive() check after monero-java 0.5.4 which nullifies internal process
daemon.stopProcess(); daemon.stopProcess();
daemon = null; for (var listener : listeners) listener.onNodeStopped();
for (var listener : listeners) listener.onNodeStopped();
} else {
defaultMoneroDaemon.stopProcess(); // throws MoneroError
}
} }
} }

View File

@ -79,7 +79,7 @@ public class AppStartupState {
if (a && b && c) { if (a && b && c) {
walletAndNetworkReady.set(true); walletAndNetworkReady.set(true);
} }
return a && b && c && d; return a && d; // app fully initialized before daemon connection and wallet by default
}); });
p2pNetworkAndWalletInitialized.subscribe((observable, oldValue, newValue) -> { p2pNetworkAndWalletInitialized.subscribe((observable, oldValue, newValue) -> {
if (newValue) { if (newValue) {

View File

@ -17,6 +17,7 @@
package bisq.core.app; package bisq.core.app;
import bisq.common.UserThread;
import bisq.core.api.CoreMoneroConnectionsService; import bisq.core.api.CoreMoneroConnectionsService;
import bisq.core.locale.Res; import bisq.core.locale.Res;
import bisq.core.provider.price.PriceFeedService; import bisq.core.provider.price.PriceFeedService;
@ -109,7 +110,7 @@ public class P2PNetworkSetup {
return result; return result;
}); });
p2PNetworkInfoBinding.subscribe((observable, oldValue, newValue) -> { p2PNetworkInfoBinding.subscribe((observable, oldValue, newValue) -> {
p2PNetworkInfo.set(newValue); UserThread.execute(() -> p2PNetworkInfo.set(newValue));
}); });
bootstrapState.set(Res.get("mainView.bootstrapState.connectionToTorNetwork")); bootstrapState.set(Res.get("mainView.bootstrapState.connectionToTorNetwork"));

View File

@ -18,6 +18,7 @@
package bisq.core.btc; package bisq.core.btc;
import bisq.common.UserThread; import bisq.common.UserThread;
import bisq.core.btc.listeners.XmrBalanceListener;
import bisq.core.btc.wallet.XmrWalletService; import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.offer.OfferPayload; import bisq.core.offer.OfferPayload;
import bisq.core.offer.OpenOffer; import bisq.core.offer.OpenOffer;
@ -41,7 +42,6 @@ import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import monero.wallet.model.MoneroOutputQuery; import monero.wallet.model.MoneroOutputQuery;
import monero.wallet.model.MoneroOutputWallet; import monero.wallet.model.MoneroOutputWallet;
import monero.wallet.model.MoneroWalletListener;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
@Slf4j @Slf4j
@ -80,18 +80,19 @@ public class Balances {
} }
public void onAllServicesInitialized() { public void onAllServicesInitialized() {
openOfferManager.getObservableList().addListener((ListChangeListener<OpenOffer>) c -> updateBalance()); openOfferManager.getObservableList().addListener((ListChangeListener<OpenOffer>) c -> updatedBalances());
tradeManager.getObservableList().addListener((ListChangeListener<Trade>) change -> updateBalance()); tradeManager.getObservableList().addListener((ListChangeListener<Trade>) change -> updatedBalances());
refundManager.getDisputesAsObservableList().addListener((ListChangeListener<Dispute>) c -> updateBalance()); refundManager.getDisputesAsObservableList().addListener((ListChangeListener<Dispute>) c -> updatedBalances());
xmrWalletService.getWallet().addListener(new MoneroWalletListener() { xmrWalletService.addBalanceListener(new XmrBalanceListener() {
@Override public void onBalancesChanged(BigInteger newBalance, BigInteger newUnlockedBalance) { updateBalance(); } @Override
@Override public void onOutputReceived(MoneroOutputWallet output) { updateBalance(); } public void onBalanceChanged(BigInteger balance) {
@Override public void onOutputSpent(MoneroOutputWallet output) { updateBalance(); } updatedBalances();
}
}); });
updateBalance(); updatedBalances();
} }
private void updateBalance() { private void updatedBalances() {
// Need to delay a bit to get the balances correct // Need to delay a bit to get the balances correct
UserThread.execute(() -> { UserThread.execute(() -> {
updateAvailableBalance(); updateAvailableBalance();
@ -105,19 +106,21 @@ public class Balances {
// TODO (woodser): balances being set as Coin from BigInteger.longValue(), which can lose precision. should be in centineros for consistency with the rest of the application // TODO (woodser): balances being set as Coin from BigInteger.longValue(), which can lose precision. should be in centineros for consistency with the rest of the application
private void updateAvailableBalance() { private void updateAvailableBalance() {
availableBalance.set(Coin.valueOf(xmrWalletService.getWallet().getUnlockedBalance(0).longValueExact())); availableBalance.set(Coin.valueOf(xmrWalletService.getWallet() == null ? 0 : xmrWalletService.getWallet().getUnlockedBalance(0).longValueExact()));
} }
private void updateLockedBalance() { private void updateLockedBalance() {
BigInteger balance = xmrWalletService.getWallet().getBalance(0); BigInteger balance = xmrWalletService.getWallet() == null ? new BigInteger("0") : xmrWalletService.getWallet().getBalance(0);
BigInteger unlockedBalance = xmrWalletService.getWallet().getUnlockedBalance(0); BigInteger unlockedBalance = xmrWalletService.getWallet() == null ? new BigInteger("0") : xmrWalletService.getWallet().getUnlockedBalance(0);
lockedBalance.set(Coin.valueOf(balance.subtract(unlockedBalance).longValueExact())); lockedBalance.set(Coin.valueOf(balance.subtract(unlockedBalance).longValueExact()));
} }
private void updateReservedOfferBalance() { private void updateReservedOfferBalance() {
Coin sum = Coin.valueOf(0); Coin sum = Coin.valueOf(0);
List<MoneroOutputWallet> frozenOutputs = xmrWalletService.getWallet().getOutputs(new MoneroOutputQuery().setIsFrozen(true).setIsSpent(false)); if (xmrWalletService.getWallet() != null) {
for (MoneroOutputWallet frozenOutput : frozenOutputs) sum = sum.add(Coin.valueOf(frozenOutput.getAmount().longValueExact())); List<MoneroOutputWallet> frozenOutputs = xmrWalletService.getWallet().getOutputs(new MoneroOutputQuery().setIsFrozen(true).setIsSpent(false));
for (MoneroOutputWallet frozenOutput : frozenOutputs) sum = sum.add(Coin.valueOf(frozenOutput.getAmount().longValueExact()));
}
reservedOfferBalance.set(sum); reservedOfferBalance.set(sum);
} }

View File

@ -76,7 +76,7 @@ public class XmrWalletService {
protected final CopyOnWriteArraySet<MoneroWalletListenerI> walletListeners = new CopyOnWriteArraySet<>(); protected final CopyOnWriteArraySet<MoneroWalletListenerI> walletListeners = new CopyOnWriteArraySet<>();
private TradeManager tradeManager; private TradeManager tradeManager;
private MoneroWallet wallet; private MoneroWalletRpc wallet;
private Map<String, MoneroWallet> multisigWallets; private Map<String, MoneroWallet> multisigWallets;
@Inject @Inject
@ -159,64 +159,6 @@ public class XmrWalletService {
return new File(path + ".keys").exists(); return new File(path + ".keys").exists();
} }
public MoneroWalletRpc createWallet(MoneroWalletConfig config, Integer port) {
// start monero-wallet-rpc instance
MoneroWalletRpc walletRpc = startWalletRpcInstance(port);
// create wallet
try {
walletRpc.createWallet(config);
walletRpc.startSyncing(MONERO_WALLET_SYNC_RATE);
return walletRpc;
} catch (Exception e) {
e.printStackTrace();
MONERO_WALLET_RPC_MANAGER.stopInstance(walletRpc, false);
throw e;
}
}
public MoneroWalletRpc openWallet(MoneroWalletConfig config, Integer port) {
// start monero-wallet-rpc instance
MoneroWalletRpc walletRpc = startWalletRpcInstance(port);
// open wallet
try {
walletRpc.openWallet(config);
walletRpc.startSyncing(MONERO_WALLET_SYNC_RATE);
return walletRpc;
} catch (Exception e) {
e.printStackTrace();
MONERO_WALLET_RPC_MANAGER.stopInstance(walletRpc, false);
throw e;
}
}
private MoneroWalletRpc startWalletRpcInstance(Integer port) {
// check if monero-wallet-rpc exists
if (!new File(MONERO_WALLET_RPC_PATH).exists()) throw new Error("monero-wallet-rpc executable doesn't exist at path " + MONERO_WALLET_RPC_PATH
+ "; copy monero-wallet-rpc to the project root or set WalletConfig.java MONERO_WALLET_RPC_PATH for your system");
// get app's current daemon connection
MoneroRpcConnection connection = connectionsService.getConnection();
// start monero-wallet-rpc instance and return connected client
List<String> cmd = new ArrayList<>(Arrays.asList( // modifiable list
MONERO_WALLET_RPC_PATH, "--" + MONERO_NETWORK_TYPE.toString().toLowerCase(), "--daemon-address", connection.getUri(), "--rpc-login",
MONERO_WALLET_RPC_USERNAME + ":" + getWalletPassword(), "--wallet-dir", walletDir.toString()));
if (connection.getUsername() != null) {
cmd.add("--daemon-login");
cmd.add(connection.getUsername() + ":" + connection.getPassword());
}
if (port != null && port > 0) {
cmd.add("--rpc-bind-port");
cmd.add(Integer.toString(port));
}
return MONERO_WALLET_RPC_MANAGER.startInstance(cmd);
}
public void closeWallet(MoneroWallet walletRpc, boolean save) { public void closeWallet(MoneroWallet walletRpc, boolean save) {
log.info("{}.closeWallet({}, {})", getClass(), walletRpc.getPath(), save); log.info("{}.closeWallet({}, {})", getClass(), walletRpc.getPath(), save);
MONERO_WALLET_RPC_MANAGER.stopInstance((MoneroWalletRpc) walletRpc, save); MONERO_WALLET_RPC_MANAGER.stopInstance((MoneroWalletRpc) walletRpc, save);
@ -290,30 +232,114 @@ public class XmrWalletService {
// backup wallet files // backup wallet files
backupWallets(); backupWallets();
// initialize main wallet // initialize main wallet if connected or previously created
MoneroWalletConfig walletConfig = new MoneroWalletConfig().setPath(MONERO_WALLET_NAME).setPassword(getWalletPassword()); tryInitMainWallet();
wallet = MoneroUtils.walletExists(xmrWalletFile.getPath()) ? openWallet(walletConfig, rpcBindPort) : createWallet(walletConfig, rpcBindPort);
System.out.println("Monero wallet path: " + wallet.getPath());
System.out.println("Monero wallet address: " + wallet.getPrimaryAddress());
System.out.println("Monero wallet uri: " + ((MoneroWalletRpc) wallet).getRpcConnection().getUri());
wallet.sync(); // blocking
connectionsService.doneDownload(); // TODO: using this to signify both daemon and wallet synced, refactor sync handling of both
wallet.save();
System.out.println("Loaded wallet balance: " + wallet.getBalance(0));
System.out.println("Loaded wallet unlocked balance: " + wallet.getUnlockedBalance(0));
// update wallet connections on change // update wallet connections on change
connectionsService.addListener(newConnection -> { connectionsService.addListener(newConnection -> {
setWalletDaemonConnections(newConnection); setWalletDaemonConnections(newConnection);
}); });
}
// notify on balance changes private void tryInitMainWallet() {
wallet.addListener(new MoneroWalletListener() { MoneroWalletConfig walletConfig = new MoneroWalletConfig().setPath(MONERO_WALLET_NAME).setPassword(getWalletPassword());
@Override if (MoneroUtils.walletExists(xmrWalletFile.getPath())) {
public void onBalancesChanged(BigInteger newBalance, BigInteger newUnlockedBalance) { wallet = openWallet(walletConfig, rpcBindPort);
notifyBalanceListeners(); } else if (connectionsService.getConnection() != null && Boolean.TRUE.equals(connectionsService.getConnection().isConnected())) {
wallet = createWallet(walletConfig, rpcBindPort); // wallet requires connection to daemon to correctly set height
}
// wallet is not initialized until connected to a daemon
if (wallet != null) {
try {
wallet.sync(); // blocking
connectionsService.doneDownload(); // TODO: using this to signify both daemon and wallet synced, refactor sync handling of both
wallet.save();
} catch (Exception e) {
e.printStackTrace();
} }
});
System.out.println("Monero wallet path: " + wallet.getPath());
System.out.println("Monero wallet address: " + wallet.getPrimaryAddress());
System.out.println("Monero wallet uri: " + wallet.getRpcConnection().getUri());
System.out.println("Monero wallet height: " + wallet.getHeight());
System.out.println("Monero wallet balance: " + wallet.getBalance(0));
System.out.println("Monero wallet unlocked balance: " + wallet.getUnlockedBalance(0));
// notify on balance changes
wallet.addListener(new MoneroWalletListener() {
@Override
public void onBalancesChanged(BigInteger newBalance, BigInteger newUnlockedBalance) {
notifyBalanceListeners();
}
});
}
}
private MoneroWalletRpc createWallet(MoneroWalletConfig config, Integer port) {
// start monero-wallet-rpc instance
MoneroWalletRpc walletRpc = startWalletRpcInstance(port);
// must be connected to daemon
MoneroRpcConnection connection = connectionsService.getConnection();
if (connection == null || !Boolean.TRUE.equals(connection.isConnected())) throw new RuntimeException("Must be connected to daemon before creating wallet");
// create wallet
try {
walletRpc.createWallet(config);
walletRpc.startSyncing(MONERO_WALLET_SYNC_RATE);
return walletRpc;
} catch (Exception e) {
e.printStackTrace();
MONERO_WALLET_RPC_MANAGER.stopInstance(walletRpc, false);
throw e;
}
}
private MoneroWalletRpc openWallet(MoneroWalletConfig config, Integer port) {
// start monero-wallet-rpc instance
MoneroWalletRpc walletRpc = startWalletRpcInstance(port);
// open wallet
try {
walletRpc.openWallet(config);
walletRpc.startSyncing(MONERO_WALLET_SYNC_RATE);
return walletRpc;
} catch (Exception e) {
e.printStackTrace();
MONERO_WALLET_RPC_MANAGER.stopInstance(walletRpc, false);
throw e;
}
}
private MoneroWalletRpc startWalletRpcInstance(Integer port) {
// check if monero-wallet-rpc exists
if (!new File(MONERO_WALLET_RPC_PATH).exists()) throw new Error("monero-wallet-rpc executable doesn't exist at path " + MONERO_WALLET_RPC_PATH
+ "; copy monero-wallet-rpc to the project root or set WalletConfig.java MONERO_WALLET_RPC_PATH for your system");
// build command to start monero-wallet-rpc
List<String> cmd = new ArrayList<>(Arrays.asList( // modifiable list
MONERO_WALLET_RPC_PATH, "--" + MONERO_NETWORK_TYPE.toString().toLowerCase(), "--rpc-login",
MONERO_WALLET_RPC_USERNAME + ":" + getWalletPassword(), "--wallet-dir", walletDir.toString()));
MoneroRpcConnection connection = connectionsService.getConnection();
if (connection != null) {
cmd.add("--daemon-address");
cmd.add(connection.getUri());
if (connection.getUsername() != null) {
cmd.add("--daemon-login");
cmd.add(connection.getUsername() + ":" + connection.getPassword());
}
}
if (port != null && port > 0) {
cmd.add("--rpc-bind-port");
cmd.add(Integer.toString(port));
}
// start monero-wallet-rpc instance and return connected client
return MONERO_WALLET_RPC_MANAGER.startInstance(cmd);
} }
private void backupWallets() { private void backupWallets() {
@ -324,6 +350,7 @@ public class XmrWalletService {
private void setWalletDaemonConnections(MoneroRpcConnection connection) { private void setWalletDaemonConnections(MoneroRpcConnection connection) {
log.info("Setting wallet daemon connections: " + (connection == null ? null : connection.getUri())); log.info("Setting wallet daemon connections: " + (connection == null ? null : connection.getUri()));
if (wallet == null) tryInitMainWallet();
if (wallet != null) wallet.setDaemonConnection(connection); if (wallet != null) wallet.setDaemonConnection(connection);
for (MoneroWallet multisigWallet : multisigWallets.values()) multisigWallet.setDaemonConnection(connection); for (MoneroWallet multisigWallet : multisigWallets.values()) multisigWallet.setDaemonConnection(connection);
} }
@ -333,7 +360,7 @@ public class XmrWalletService {
Coin balance; Coin balance;
if (balanceListener.getSubaddressIndex() != null && balanceListener.getSubaddressIndex() != 0) balance = getBalanceForSubaddress(balanceListener.getSubaddressIndex()); if (balanceListener.getSubaddressIndex() != null && balanceListener.getSubaddressIndex() != 0) balance = getBalanceForSubaddress(balanceListener.getSubaddressIndex());
else balance = getAvailableConfirmedBalance(); else balance = getAvailableConfirmedBalance();
UserThread.execute(new Runnable() { UserThread.execute(new Runnable() { // TODO (woodser): don't execute on UserThread
@Override @Override
public void run() { public void run() {
balanceListener.onBalanceChanged(BigInteger.valueOf(balance.value)); balanceListener.onBalanceChanged(BigInteger.valueOf(balance.value));
@ -549,6 +576,7 @@ public class XmrWalletService {
return available.filter(addressEntry -> getBalanceForSubaddress(addressEntry.getSubaddressIndex()).isPositive()); return available.filter(addressEntry -> getBalanceForSubaddress(addressEntry.getSubaddressIndex()).isPositive());
} }
// TODO (woodser): update balance and other listening
public void addBalanceListener(XmrBalanceListener listener) { public void addBalanceListener(XmrBalanceListener listener) {
balanceListeners.add(listener); balanceListeners.add(listener);
} }