manage monero connections from ui #623

use tor for all monero connections by default
renames from btc to xmr
updated translations
This commit is contained in:
woodser 2023-06-05 12:55:32 -04:00
parent 1faedc8121
commit 06be2a4829
45 changed files with 681 additions and 663 deletions

View file

@ -1,10 +1,13 @@
package haveno.core.api;
import haveno.common.app.DevEnv;
import haveno.common.config.BaseCurrencyNetwork;
import haveno.common.config.Config;
import haveno.core.trade.HavenoUtils;
import haveno.core.user.Preferences;
import haveno.core.xmr.model.EncryptedConnectionList;
import haveno.core.xmr.nodes.XmrNodes;
import haveno.core.xmr.nodes.XmrNodesSetupPreferences;
import haveno.core.xmr.nodes.XmrNodes.XmrNode;
import haveno.core.xmr.setup.DownloadListener;
import haveno.core.xmr.setup.WalletsSetup;
import haveno.network.Socks5ProxyProvider;
@ -32,10 +35,7 @@ import monero.daemon.model.MoneroPeer;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
@ -50,38 +50,14 @@ public final class CoreMoneroConnectionsService {
private static final long MIN_ERROR_LOG_PERIOD_MS = 300000; // minimum period between logging errors fetching daemon info
private static Long lastErrorTimestamp;
// default Monero nodes
private static final Map<BaseCurrencyNetwork, List<MoneroRpcConnection>> DEFAULT_CONNECTIONS;
static {
DEFAULT_CONNECTIONS = new HashMap<BaseCurrencyNetwork, List<MoneroRpcConnection>>();
DEFAULT_CONNECTIONS.put(BaseCurrencyNetwork.XMR_LOCAL, Arrays.asList(
new MoneroRpcConnection("http://127.0.0.1:28081").setPriority(1)
));
DEFAULT_CONNECTIONS.put(BaseCurrencyNetwork.XMR_STAGENET, Arrays.asList(
new MoneroRpcConnection("http://127.0.0.1:38081").setPriority(1), // localhost is first priority, use loopback address 127.0.0.1 to match url used by local node service
new MoneroRpcConnection("http://127.0.0.1:39081").setPriority(2), // from makefile: `monerod-stagenet-custom`
new MoneroRpcConnection("http://45.63.8.26:38081").setPriority(2), // hosted by haveno
new MoneroRpcConnection("http://stagenet.community.rino.io:38081").setPriority(2),
new MoneroRpcConnection("http://stagenet.melo.tools:38081").setPriority(2),
new MoneroRpcConnection("http://node.sethforprivacy.com:38089").setPriority(2),
new MoneroRpcConnection("http://node2.sethforprivacy.com:38089").setPriority(2),
new MoneroRpcConnection("http://plowsof3t5hogddwabaeiyrno25efmzfxyro2vligremt7sxpsclfaid.onion:38089").setPriority(2)
));
DEFAULT_CONNECTIONS.put(BaseCurrencyNetwork.XMR_MAINNET, Arrays.asList(
new MoneroRpcConnection("http://127.0.0.1:18081").setPriority(1),
new MoneroRpcConnection("http://node.community.rino.io:18081").setPriority(2),
new MoneroRpcConnection("http://xmr-node.cakewallet.com:18081").setPriority(2),
new MoneroRpcConnection("http://xmr-node-eu.cakewallet.com:18081").setPriority(2),
new MoneroRpcConnection("http://xmr-node-usa-east.cakewallet.com:18081").setPriority(2),
new MoneroRpcConnection("http://xmr-node-uk.cakewallet.com:18081").setPriority(2),
new MoneroRpcConnection("http://node.sethforprivacy.com:18089").setPriority(2)
));
}
private final Object lock = new Object();
private final Config config;
private final CoreContext coreContext;
private final Preferences preferences;
private final CoreAccountService accountService;
private final XmrNodes xmrNodes;
private final CoreMoneroNodeService nodeService;
private final MoneroConnectionManager connectionManager;
private final EncryptedConnectionList connectionList;
@ -103,15 +79,19 @@ public final class CoreMoneroConnectionsService {
public CoreMoneroConnectionsService(P2PService p2PService,
Config config,
CoreContext coreContext,
Preferences preferences,
WalletsSetup walletsSetup,
CoreAccountService accountService,
XmrNodes xmrNodes,
CoreMoneroNodeService nodeService,
MoneroConnectionManager connectionManager,
EncryptedConnectionList connectionList,
Socks5ProxyProvider socks5ProxyProvider) {
this.config = config;
this.coreContext = coreContext;
this.preferences = preferences;
this.accountService = accountService;
this.xmrNodes = xmrNodes;
this.nodeService = nodeService;
this.connectionManager = connectionManager;
this.connectionList = connectionList;
@ -178,7 +158,7 @@ public final class CoreMoneroConnectionsService {
public void addConnection(MoneroRpcConnection connection) {
synchronized (lock) {
accountService.checkAccountOpen();
connectionList.addConnection(connection);
if (coreContext.isApiUser()) connectionList.addConnection(connection);
connectionManager.addConnection(connection);
}
}
@ -342,7 +322,7 @@ public final class CoreMoneroConnectionsService {
if (isConnectionLocal()) {
if (lastInfo != null && (lastInfo.isBusySyncing() || (lastInfo.getHeightWithoutBootstrap() != null && lastInfo.getHeightWithoutBootstrap() > 0 && lastInfo.getHeightWithoutBootstrap() < lastInfo.getHeight()))) return REFRESH_PERIOD_HTTP_MS; // refresh slower if syncing or bootstrapped
else return REFRESH_PERIOD_LOCAL_MS; // TODO: announce faster refresh after done syncing
} else if (getConnection().isOnion()) {
} else if (useProxy(getConnection())) {
return REFRESH_PERIOD_ONION_MS;
} else {
return REFRESH_PERIOD_HTTP_MS;
@ -350,6 +330,10 @@ public final class CoreMoneroConnectionsService {
}
}
private boolean useProxy(MoneroRpcConnection connection) {
return connection.isOnion() || (preferences.isUseTorForMonero() && !HavenoUtils.isLocalHost(connection.getUri()));
}
private void initialize() {
// initialize connections
@ -384,35 +368,61 @@ public final class CoreMoneroConnectionsService {
connectionManager.reset();
connectionManager.setTimeout(REFRESH_PERIOD_HTTP_MS);
// load connections
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");
// load previous or default connections
if (coreContext.isApiUser()) {
// add default connections
for (MoneroRpcConnection connection : DEFAULT_CONNECTIONS.get(Config.baseCurrencyNetwork())) {
if (connectionList.hasConnection(connection.getUri())) continue;
if (connection.isOnion()) connection.setProxyUri(getProxyUri());
addConnection(connection);
// 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 used connection if unconfigured and present
Optional<String> currentConnectionUri = null;
// set current connection
Optional<String> currentConnectionUri = Optional.empty();
if ("".equals(config.xmrNode)) {
currentConnectionUri = connectionList.getCurrentConnectionUri();
if (currentConnectionUri.isPresent()) connectionManager.setConnection(currentConnectionUri.get());
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 (connection.isOnion()) connection.setProxyUri(getProxyUri());
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());

View file

@ -95,7 +95,7 @@ public class WalletAppSetup {
this.connectionService = connectionService;
this.config = config;
this.preferences = preferences;
this.useTorForBTC.set(preferences.getUseTorForBitcoinJ());
this.useTorForBTC.set(preferences.getUseTorForMonero());
}
void init(@Nullable Consumer<String> chainFileLockedExceptionHandler,
@ -259,9 +259,9 @@ public class WalletAppSetup {
private String getBtcNetworkAsString() {
String postFix;
if (config.ignoreLocalBtcNode)
if (config.ignoreLocalXmrNode)
postFix = " " + Res.get("mainView.footer.localhostBitcoinNode");
else if (preferences.getUseTorForBitcoinJ())
else if (preferences.getUseTorForMonero())
postFix = " " + Res.get("mainView.footer.usingTor");
else
postFix = "";

View file

@ -53,7 +53,7 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
private final List<String> seedNodes;
private final List<String> priceRelayNodes;
private final boolean preventPublicBtcNetwork;
private final List<String> btcNodes;
private final List<String> xmrNodes;
// SignatureAsBase64 is not set initially as we use the serialized data for signing. We set it after signature is
// created by cloning the object with a non-null sig.
@Nullable
@ -104,7 +104,7 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
filter.getSeedNodes(),
filter.getPriceRelayNodes(),
filter.isPreventPublicBtcNetwork(),
filter.getBtcNodes(),
filter.getXmrNodes(),
filter.getDisableTradeBelowVersion(),
filter.getMediators(),
filter.getRefundAgents(),
@ -134,7 +134,7 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
filter.getSeedNodes(),
filter.getPriceRelayNodes(),
filter.isPreventPublicBtcNetwork(),
filter.getBtcNodes(),
filter.getXmrNodes(),
filter.getDisableTradeBelowVersion(),
filter.getMediators(),
filter.getRefundAgents(),
@ -162,7 +162,7 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
List<String> seedNodes,
List<String> priceRelayNodes,
boolean preventPublicBtcNetwork,
List<String> btcNodes,
List<String> xmrNodes,
String disableTradeBelowVersion,
List<String> mediators,
List<String> refundAgents,
@ -185,7 +185,7 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
seedNodes,
priceRelayNodes,
preventPublicBtcNetwork,
btcNodes,
xmrNodes,
disableTradeBelowVersion,
mediators,
refundAgents,
@ -219,7 +219,7 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
List<String> seedNodes,
List<String> priceRelayNodes,
boolean preventPublicBtcNetwork,
List<String> btcNodes,
List<String> xmrNodes,
String disableTradeBelowVersion,
List<String> mediators,
List<String> refundAgents,
@ -245,7 +245,7 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
this.seedNodes = seedNodes;
this.priceRelayNodes = priceRelayNodes;
this.preventPublicBtcNetwork = preventPublicBtcNetwork;
this.btcNodes = btcNodes;
this.xmrNodes = xmrNodes;
this.disableTradeBelowVersion = disableTradeBelowVersion;
this.mediators = mediators;
this.refundAgents = refundAgents;
@ -286,7 +286,7 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
.addAllSeedNodes(seedNodes)
.addAllPriceRelayNodes(priceRelayNodes)
.setPreventPublicBtcNetwork(preventPublicBtcNetwork)
.addAllBtcNodes(btcNodes)
.addAllXmrNodes(xmrNodes)
.setDisableTradeBelowVersion(disableTradeBelowVersion)
.addAllMediators(mediators)
.addAllRefundAgents(refundAgents)
@ -323,7 +323,7 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
ProtoUtil.protocolStringListToList(proto.getSeedNodesList()),
ProtoUtil.protocolStringListToList(proto.getPriceRelayNodesList()),
proto.getPreventPublicBtcNetwork(),
ProtoUtil.protocolStringListToList(proto.getBtcNodesList()),
ProtoUtil.protocolStringListToList(proto.getXmrNodesList()),
proto.getDisableTradeBelowVersion(),
ProtoUtil.protocolStringListToList(proto.getMediatorsList()),
ProtoUtil.protocolStringListToList(proto.getRefundAgentsList()),
@ -366,7 +366,7 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
",\n seedNodes=" + seedNodes +
",\n priceRelayNodes=" + priceRelayNodes +
",\n preventPublicBtcNetwork=" + preventPublicBtcNetwork +
",\n btcNodes=" + btcNodes +
",\n xmrNodes=" + xmrNodes +
",\n signatureAsBase64='" + signatureAsBase64 + '\'' +
",\n signerPubKeyAsHex='" + signerPubKeyAsHex + '\'' +
",\n ownerPubKeyBytes=" + Utilities.bytesAsHexString(ownerPubKeyBytes) +

View file

@ -28,7 +28,7 @@ import haveno.core.payment.payload.PaymentMethod;
import haveno.core.provider.ProvidersRepository;
import haveno.core.user.Preferences;
import haveno.core.user.User;
import haveno.core.xmr.nodes.BtcNodes;
import haveno.core.xmr.nodes.XmrNodes;
import haveno.network.p2p.NodeAddress;
import haveno.network.p2p.P2PService;
import haveno.network.p2p.P2PServiceListener;
@ -69,7 +69,7 @@ import static org.bitcoinj.core.Utils.HEX;
public class FilterManager {
private static final String BANNED_PRICE_RELAY_NODES = "bannedPriceRelayNodes";
private static final String BANNED_SEED_NODES = "bannedSeedNodes";
private static final String BANNED_BTC_NODES = "bannedBtcNodes";
private static final String BANNED_XMR_NODES = "bannedXmrNodes";
///////////////////////////////////////////////////////////////////////////////////////////
// Listener
@ -497,7 +497,7 @@ public class FilterManager {
// nodes at the next startup and don't update the list in the P2P network domain.
// We persist it to the property file which is read before any other initialisation.
saveBannedNodes(BANNED_SEED_NODES, newFilter.getSeedNodes());
saveBannedNodes(BANNED_BTC_NODES, newFilter.getBtcNodes());
saveBannedNodes(BANNED_XMR_NODES, newFilter.getXmrNodes());
// Banned price relay nodes we can apply at runtime
List<String> priceRelayNodes = newFilter.getPriceRelayNodes();
@ -508,8 +508,8 @@ public class FilterManager {
//TODO should be moved to client with listening on onFilterAdded
if (newFilter.isPreventPublicBtcNetwork() &&
preferences.getBitcoinNodesOptionOrdinal() == BtcNodes.BitcoinNodesOption.PUBLIC.ordinal()) {
preferences.setBitcoinNodesOptionOrdinal(BtcNodes.BitcoinNodesOption.PROVIDED.ordinal());
preferences.getMoneroNodesOptionOrdinal() == XmrNodes.MoneroNodesOption.PUBLIC.ordinal()) {
preferences.setMoneroNodesOptionOrdinal(XmrNodes.MoneroNodesOption.PROVIDED.ordinal());
}
listeners.forEach(e -> e.onFilterAdded(newFilter));
@ -541,7 +541,7 @@ public class FilterManager {
// Clears options files from banned nodes
private void clearBannedNodes() {
saveBannedNodes(BANNED_BTC_NODES, null);
saveBannedNodes(BANNED_XMR_NODES, null);
saveBannedNodes(BANNED_SEED_NODES, null);
saveBannedNodes(BANNED_PRICE_RELAY_NODES, null);

View file

@ -32,7 +32,7 @@ import haveno.core.locale.TradeCurrency;
import haveno.core.payment.PaymentAccount;
import haveno.core.payment.PaymentAccountUtil;
import haveno.core.xmr.MoneroNodeSettings;
import haveno.core.xmr.nodes.BtcNodes;
import haveno.core.xmr.nodes.XmrNodes;
import haveno.core.xmr.nodes.LocalBitcoinNode;
import haveno.core.xmr.wallet.Restrictions;
import haveno.network.p2p.network.BridgeAddressProvider;
@ -154,7 +154,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
private final PersistenceManager<PreferencesPayload> persistenceManager;
private final Config config;
private final LocalBitcoinNode localBitcoinNode;
private final String btcNodesFromOptions;
private final String xmrNodesFromOptions;
@Getter
private final BooleanProperty useStandbyModeProperty = new SimpleBooleanProperty(prefPayload.isUseStandbyMode());
@ -166,12 +166,12 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
public Preferences(PersistenceManager<PreferencesPayload> persistenceManager,
Config config,
LocalBitcoinNode localBitcoinNode,
@Named(Config.BTC_NODES) String btcNodesFromOptions) {
@Named(Config.XMR_NODES) String xmrNodesFromOptions) {
this.persistenceManager = persistenceManager;
this.config = config;
this.localBitcoinNode = localBitcoinNode;
this.btcNodesFromOptions = btcNodesFromOptions;
this.xmrNodesFromOptions = xmrNodesFromOptions;
useAnimationsProperty.addListener((ov) -> {
prefPayload.setUseAnimations(useAnimationsProperty.get());
@ -303,16 +303,16 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
dontShowAgainMapAsObservable.putAll(getDontShowAgainMap());
// Override settings with options if set
if (config.useTorForBtcOptionSetExplicitly)
setUseTorForBitcoinJ(config.useTorForBtc);
if (config.useTorForXmrOptionSetExplicitly)
setUseTorForMonero(config.useTorForXmr);
if (btcNodesFromOptions != null && !btcNodesFromOptions.isEmpty()) {
if (getBitcoinNodes() != null && !getBitcoinNodes().equals(btcNodesFromOptions)) {
if (xmrNodesFromOptions != null && !xmrNodesFromOptions.isEmpty()) {
if (getMoneroNodes() != null && !getMoneroNodes().equals(xmrNodesFromOptions)) {
log.warn("The Bitcoin node(s) from the program argument and the one(s) persisted in the UI are different. " +
"The Bitcoin node(s) {} from the program argument will be used.", btcNodesFromOptions);
"The Bitcoin node(s) {} from the program argument will be used.", xmrNodesFromOptions);
}
setBitcoinNodes(btcNodesFromOptions);
setBitcoinNodesOptionOrdinal(BtcNodes.BitcoinNodesOption.CUSTOM.ordinal());
setMoneroNodes(xmrNodesFromOptions);
setMoneroNodesOptionOrdinal(XmrNodes.MoneroNodesOption.CUSTOM.ordinal());
}
if (prefPayload.getIgnoreDustThreshold() < Restrictions.getMinNonDustOutput().value) {
@ -492,8 +492,8 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
}
}
public void setUseTorForBitcoinJ(boolean useTorForBitcoinJ) {
prefPayload.setUseTorForBitcoinJ(useTorForBitcoinJ);
public void setUseTorForMonero(boolean useTorForMonero) {
prefPayload.setUseTorForMonero(useTorForMonero);
requestPersistence();
}
@ -577,8 +577,8 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
requestPersistence();
}
public void setBitcoinNodes(String bitcoinNodes) {
prefPayload.setBitcoinNodes(bitcoinNodes);
public void setMoneroNodes(String bitcoinNodes) {
prefPayload.setMoneroNodes(bitcoinNodes);
requestPersistence();
}
@ -663,8 +663,8 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
persistenceManager.forcePersistNow();
}
public void setBitcoinNodesOptionOrdinal(int bitcoinNodesOptionOrdinal) {
prefPayload.setBitcoinNodesOptionOrdinal(bitcoinNodesOptionOrdinal);
public void setMoneroNodesOptionOrdinal(int bitcoinNodesOptionOrdinal) {
prefPayload.setMoneroNodesOptionOrdinal(bitcoinNodesOptionOrdinal);
requestPersistence();
}
@ -793,18 +793,8 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
return !prefPayload.getDontShowAgainMap().containsKey(key) || !prefPayload.getDontShowAgainMap().get(key);
}
public boolean getUseTorForBitcoinJ() {
// We override the useTorForBitcoinJ and set it to false if we will use a
// localhost Bitcoin node or if we are not on mainnet, unless the useTorForBtc
// parameter is explicitly provided. On testnet there are very few Bitcoin tor
// nodes and we don't provide tor nodes.
if ((!Config.baseCurrencyNetwork().isMainnet()
|| localBitcoinNode.shouldBeUsed())
&& !config.useTorForBtcOptionSetExplicitly)
return false;
else
return prefPayload.isUseTorForBitcoinJ();
public boolean getUseTorForMonero() {
return prefPayload.isUseTorForMonero();
}
public double getBuyerSecurityDepositAsPercent(PaymentAccount paymentAccount) {
@ -869,7 +859,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
void setPreferredTradeCurrency(TradeCurrency preferredTradeCurrency);
void setUseTorForBitcoinJ(boolean useTorForBitcoinJ);
void setUseTorForMonero(boolean useTorForMonero);
void setShowOwnOffersInOfferBook(boolean showOwnOffersInOfferBook);
@ -899,7 +889,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
void setSortMarketCurrenciesNumerically(boolean sortMarketCurrenciesNumerically);
void setBitcoinNodes(String bitcoinNodes);
void setMoneroNodes(String bitcoinNodes);
void setUseCustomWithdrawalTxFee(boolean useCustomWithdrawalTxFee);
@ -931,7 +921,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
void setCustomBridges(String customBridges);
void setBitcoinNodesOptionOrdinal(int bitcoinNodesOption);
void setMoneroNodesOptionOrdinal(int bitcoinNodesOption);
void setReferralId(String referralId);

View file

@ -19,6 +19,7 @@ package haveno.core.user;
import com.google.common.collect.Maps;
import com.google.protobuf.Message;
import haveno.common.proto.ProtoUtil;
import haveno.common.proto.persistable.PersistableEnvelope;
import haveno.core.locale.Country;
@ -57,7 +58,7 @@ public final class PreferencesPayload implements PersistableEnvelope {
private boolean autoSelectArbitrators = true;
private Map<String, Boolean> dontShowAgainMap = new HashMap<>();
private boolean tacAccepted;
private boolean useTorForBitcoinJ = true;
private boolean useTorForMonero = true;
private boolean showOwnOffersInOfferBook = true;
@Nullable
private TradeCurrency preferredTradeCurrency;
@ -81,8 +82,8 @@ public final class PreferencesPayload implements PersistableEnvelope {
private boolean sortMarketCurrenciesNumerically = true;
private boolean usePercentageBasedPrice = true;
private Map<String, String> peerTagMap = new HashMap<>();
// custom btc nodes
private String bitcoinNodes = "";
// custom xmr nodes
private String moneroNodes = "";
private List<String> ignoreTradersList = new ArrayList<>();
private String directoryChooserPath;
@ -96,7 +97,7 @@ public final class PreferencesPayload implements PersistableEnvelope {
private int torTransportOrdinal;
@Nullable
private String customBridges;
private int bitcoinNodesOptionOrdinal;
private int moneroNodesOptionOrdinal;
@Nullable
private String referralId;
@Nullable
@ -159,7 +160,7 @@ public final class PreferencesPayload implements PersistableEnvelope {
.setAutoSelectArbitrators(autoSelectArbitrators)
.putAllDontShowAgainMap(dontShowAgainMap)
.setTacAccepted(tacAccepted)
.setUseTorForBitcoinJ(useTorForBitcoinJ)
.setUseTorForMonero(useTorForMonero)
.setShowOwnOffersInOfferBook(showOwnOffersInOfferBook)
.setWithdrawalTxFeeInVbytes(withdrawalTxFeeInVbytes)
.setUseCustomWithdrawalTxFee(useCustomWithdrawalTxFee)
@ -169,14 +170,14 @@ public final class PreferencesPayload implements PersistableEnvelope {
.setSortMarketCurrenciesNumerically(sortMarketCurrenciesNumerically)
.setUsePercentageBasedPrice(usePercentageBasedPrice)
.putAllPeerTagMap(peerTagMap)
.setBitcoinNodes(bitcoinNodes)
.setMoneroNodes(moneroNodes)
.addAllIgnoreTradersList(ignoreTradersList)
.setDirectoryChooserPath(directoryChooserPath)
.setUseAnimations(useAnimations)
.setCssTheme(cssTheme)
.setBridgeOptionOrdinal(bridgeOptionOrdinal)
.setTorTransportOrdinal(torTransportOrdinal)
.setBitcoinNodesOptionOrdinal(bitcoinNodesOptionOrdinal)
.setMoneroNodesOptionOrdinal(moneroNodesOptionOrdinal)
.setUseSoundForMobileNotifications(useSoundForMobileNotifications)
.setUseTradeNotifications(useTradeNotifications)
.setUseMarketNotifications(useMarketNotifications)
@ -241,7 +242,7 @@ public final class PreferencesPayload implements PersistableEnvelope {
proto.getAutoSelectArbitrators(),
Maps.newHashMap(proto.getDontShowAgainMapMap()),
proto.getTacAccepted(),
proto.getUseTorForBitcoinJ(),
proto.getUseTorForMonero(),
proto.getShowOwnOffersInOfferBook(),
proto.hasPreferredTradeCurrency() ? TradeCurrency.fromProto(proto.getPreferredTradeCurrency()) : null,
proto.getWithdrawalTxFeeInVbytes(),
@ -258,7 +259,7 @@ public final class PreferencesPayload implements PersistableEnvelope {
proto.getSortMarketCurrenciesNumerically(),
proto.getUsePercentageBasedPrice(),
Maps.newHashMap(proto.getPeerTagMapMap()),
proto.getBitcoinNodes(),
proto.getMoneroNodes(),
proto.getIgnoreTradersListList(),
proto.getDirectoryChooserPath(),
proto.getUseAnimations(),
@ -268,7 +269,7 @@ public final class PreferencesPayload implements PersistableEnvelope {
proto.getBridgeOptionOrdinal(),
proto.getTorTransportOrdinal(),
ProtoUtil.stringOrNullFromProto(proto.getCustomBridges()),
proto.getBitcoinNodesOptionOrdinal(),
proto.getMoneroNodesOptionOrdinal(),
proto.getReferralId().isEmpty() ? null : proto.getReferralId(),
proto.getPhoneKeyAndToken().isEmpty() ? null : proto.getPhoneKeyAndToken(),
proto.getUseSoundForMobileNotifications(),

View file

@ -27,7 +27,7 @@ import haveno.core.provider.price.PriceFeedService;
import haveno.core.xmr.model.AddressEntryList;
import haveno.core.xmr.model.EncryptedConnectionList;
import haveno.core.xmr.model.XmrAddressEntryList;
import haveno.core.xmr.nodes.BtcNodes;
import haveno.core.xmr.nodes.XmrNodes;
import haveno.core.xmr.setup.RegTestHost;
import haveno.core.xmr.setup.WalletsSetup;
import haveno.core.xmr.wallet.BtcWalletService;
@ -74,7 +74,7 @@ public class MoneroModule extends AppModule {
bindConstant().annotatedWith(named(Config.XMR_NODE)).to(config.xmrNode);
bindConstant().annotatedWith(named(Config.XMR_NODE_USERNAME)).to(config.xmrNodeUsername);
bindConstant().annotatedWith(named(Config.XMR_NODE_PASSWORD)).to(config.xmrNodePassword);
bindConstant().annotatedWith(named(Config.BTC_NODES)).to(config.btcNodes);
bindConstant().annotatedWith(named(Config.XMR_NODES)).to(config.xmrNodes);
bindConstant().annotatedWith(named(Config.USER_AGENT)).to(config.userAgent);
bindConstant().annotatedWith(named(Config.NUM_CONNECTIONS_FOR_BTC)).to(config.numConnectionsForBtc);
bindConstant().annotatedWith(named(Config.USE_ALL_PROVIDED_NODES)).to(config.useAllProvidedNodes);
@ -89,7 +89,7 @@ public class MoneroModule extends AppModule {
bind(BtcWalletService.class).in(Singleton.class);
bind(TradeWalletService.class).in(Singleton.class);
bind(NonBsqCoinSelector.class).in(Singleton.class);
bind(BtcNodes.class).in(Singleton.class);
bind(XmrNodes.class).in(Singleton.class);
bind(Balances.class).in(Singleton.class);
bind(ProvidersRepository.class).in(Singleton.class);

View file

@ -148,7 +148,7 @@ public class EncryptedConnectionList implements PersistableEnvelope, PersistedDa
writeLock.unlock();
}
if (currentValue != null) {
throw new IllegalStateException(String.format("There exists already an connection for \"%s\"", connection.getUri()));
throw new IllegalStateException(String.format("There exists already a connection for \"%s\"", connection.getUri()));
}
requestPersistence();
}

View file

@ -1,178 +0,0 @@
/*
* This file is part of Haveno.
*
* Haveno is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Haveno is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package haveno.core.xmr.nodes;
import haveno.common.config.Config;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument;
// Managed here: https://github.com/bisq-network/roles/issues/39
@Slf4j
public class BtcNodes {
public enum BitcoinNodesOption {
PROVIDED,
CUSTOM,
PUBLIC
}
// For other base currencies or testnet we ignore provided nodes
public List<BtcNode> getProvidedBtcNodes() {
return useProvidedBtcNodes() ?
Arrays.asList(
// emzy
new BtcNode("btcnode1.emzy.de", "emzybtc3ewh7zihpkdvuwlgxrhzcxy2p5fvjggp7ngjbxcytxvt4rjid.onion", "167.86.90.239", BtcNode.DEFAULT_PORT, "@emzy"),
new BtcNode("btcnode2.emzy.de", "emzybtc25oddoa2prol2znpz2axnrg6k77xwgirmhv7igoiucddsxiad.onion", "62.171.129.32", BtcNode.DEFAULT_PORT, "@emzy"),
new BtcNode("btcnode3.emzy.de", "emzybtc5bnpb2o6gh54oquiox54o4r7yn4a2wiiwzrjonlouaibm2zid.onion", "136.243.53.40", BtcNode.DEFAULT_PORT, "@emzy"),
new BtcNode("btcnode4.emzy.de", "emzybtc454ewbviqnmgtgx3rgublsgkk23r4onbhidcv36wremue4kqd.onion", "135.181.215.237", BtcNode.DEFAULT_PORT, "@emzy"),
// ripcurlx
new BtcNode("bitcoin.christophatteneder.com", "catlnkpdm454ecngktyo4z22m5dlcvfvgzz4nt5l36eeczecrafslkqd.onion", "174.138.35.229", BtcNode.DEFAULT_PORT, "@Christoph"),
// mrosseel
new BtcNode("btc.vante.me", "bsqbtctulf2g4jtjsdfgl2ed7qs6zz5wqx27qnyiik7laockryvszqqd.onion", "94.23.21.80", BtcNode.DEFAULT_PORT, "@miker"),
new BtcNode("btc2.vante.me", "bsqbtcparrfihlwolt4xgjbf4cgqckvrvsfyvy6vhiqrnh4w6ghixoid.onion", "94.23.205.110", BtcNode.DEFAULT_PORT, "@miker"),
// sqrrm
new BtcNode("btc1.sqrrm.net", "jygcc54etaubgdpcvzgbihjaqbc37cstpvum5sjzvka4bibkp4wrgnqd.onion", "185.25.48.184", BtcNode.DEFAULT_PORT, "@sqrrm"),
new BtcNode("btc2.sqrrm.net", "h32haomoe52ljz6qopedsocvotvoj5lm2zmecfhdhawb3flbsf64l2qd.onion", "81.171.22.143", BtcNode.DEFAULT_PORT, "@sqrrm"),
// KanoczTomas
new BtcNode("btc.ispol.sk", "mbm6ffx6j5ygi2ck.onion", "193.58.196.212", BtcNode.DEFAULT_PORT, "@KanoczTomas"),
// Devin Bileck
new BtcNode("btc1.haveno.services", "devinbtctu7uctl7hly2juu3thbgeivfnvw3ckj3phy6nyvpnx66yeyd.onion", "172.105.21.216", BtcNode.DEFAULT_PORT, "@devinbileck"),
new BtcNode("btc2.haveno.services", "devinbtcyk643iruzfpaxw3on2jket7rbjmwygm42dmdyub3ietrbmid.onion", "173.255.240.205", BtcNode.DEFAULT_PORT, "@devinbileck"),
new BtcNode(null, "devinbtcmwkuitvxl3tfi5of4zau46ymeannkjv6fpnylkgf3q5fa3id.onion", null, BtcNode.DEFAULT_PORT, "@devinbileck"),
// m52go
new BtcNode(null, "rss2u4embqa6fzuaiuvpp6szklkdckrcfgv6zer3ngclkzclvstywjyd.onion", null, BtcNode.DEFAULT_PORT, "@m52go"),
// wiz
new BtcNode("node130.hnl.wiz.biz", "wizbit5555bsslwv4ctronnsgk5vh2w2pdx7v7eyuivlyuoteejk7lid.onion", "103.99.168.130", BtcNode.DEFAULT_PORT, "@wiz"),
new BtcNode("node140.hnl.wiz.biz", "jto2jfbsxhb6yvhcrrjddrgbakte6tgsy3c3z3prss64gndgvovvosyd.onion", "103.99.168.140", BtcNode.DEFAULT_PORT, "@wiz"),
new BtcNode("node210.fmt.wiz.biz", "rfqmn3qe36uaptkxhdvi74p4hyrzhir6vhmzb2hqryxodig4gue2zbyd.onion", "103.99.170.210", BtcNode.DEFAULT_PORT, "@wiz"),
new BtcNode("node220.fmt.wiz.biz", "azbpsh4arqlm6442wfimy7qr65bmha2zhgjg7wbaji6vvaug53hur2qd.onion", "103.99.170.220", BtcNode.DEFAULT_PORT, "@wiz")
) :
new ArrayList<>();
}
public boolean useProvidedBtcNodes() {
return Config.baseCurrencyNetwork().isMainnet();
}
public static List<BtcNodes.BtcNode> toBtcNodesList(Collection<String> nodes) {
return nodes.stream()
.filter(e -> !e.isEmpty())
.map(BtcNodes.BtcNode::fromFullAddress)
.collect(Collectors.toList());
}
@EqualsAndHashCode
@Getter
public static class BtcNode {
private static final int DEFAULT_PORT = Config.baseCurrencyNetworkParameters().getPort(); //8333
@Nullable
private final String onionAddress;
@Nullable
private final String hostName;
@Nullable
private final String operator; // null in case the user provides a list of custom btc nodes
@Nullable
private final String address; // IPv4 address
private int port = DEFAULT_PORT;
/**
* @param fullAddress [IPv4 address:port or onion:port]
* @return BtcNode instance
*/
public static BtcNode fromFullAddress(String fullAddress) {
String[] parts = fullAddress.split("]");
checkArgument(parts.length > 0);
String host = "";
int port = DEFAULT_PORT;
if (parts[0].contains("[") && parts[0].contains(":")) {
// IPv6 address and optional port number
// address part delimited by square brackets e.g. [2a01:123:456:789::2]:8333
host = parts[0].replace("[", "").replace("]", "");
if (parts.length == 2)
port = Integer.parseInt(parts[1].replace(":", ""));
} else if (parts[0].contains(":") && !parts[0].contains(".")) {
// IPv6 address only; not delimited by square brackets
host = parts[0];
} else if (parts[0].contains(".")) {
// address and an optional port number
// e.g. 127.0.0.1:8333 or abcdef123xyz.onion:9999
parts = fullAddress.split(":");
checkArgument(parts.length > 0);
host = parts[0];
if (parts.length == 2)
port = Integer.parseInt(parts[1]);
}
checkArgument(host.length() > 0, "BtcNode address format not recognised");
return host.contains(".onion") ? new BtcNode(null, host, null, port, null) : new BtcNode(null, null, host, port, null);
}
public BtcNode(@Nullable String hostName,
@Nullable String onionAddress,
@Nullable String address,
int port,
@Nullable String operator) {
this.hostName = hostName;
this.onionAddress = onionAddress;
this.address = address;
this.port = port;
this.operator = operator;
}
public boolean hasOnionAddress() {
return onionAddress != null;
}
public String getHostNameOrAddress() {
if (hostName != null)
return hostName;
else
return address;
}
public boolean hasClearNetAddress() {
return hostName != null || address != null;
}
@Override
public String toString() {
return "onionAddress='" + onionAddress + '\'' +
", hostName='" + hostName + '\'' +
", address='" + address + '\'' +
", port='" + port + '\'' +
", operator='" + operator;
}
}
}

View file

@ -15,7 +15,7 @@ import java.net.Socket;
/**
* Detects whether a Bitcoin node is running on localhost and contains logic for when to
* ignore it. The query methods lazily trigger the needed checks and cache the results.
* @see haveno.common.config.Config#ignoreLocalBtcNode
* @see haveno.common.config.Config#ignoreLocalXmrNode
*/
@Singleton
public class LocalBitcoinNode {
@ -49,7 +49,7 @@ public class LocalBitcoinNode {
*/
public boolean shouldBeIgnored() {
BaseCurrencyNetwork baseCurrencyNetwork = config.baseCurrencyNetwork;
return config.ignoreLocalBtcNode;
return config.ignoreLocalXmrNode;
}
/**

View file

@ -30,8 +30,8 @@ import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.List;
public class BtcNetworkConfig {
private static final Logger log = LoggerFactory.getLogger(BtcNetworkConfig.class);
public class XmrNetworkConfig {
private static final Logger log = LoggerFactory.getLogger(XmrNetworkConfig.class);
@Nullable
private final Socks5Proxy proxy;
@ -39,7 +39,7 @@ public class BtcNetworkConfig {
private final NetworkParameters parameters;
private final int socks5DiscoverMode;
public BtcNetworkConfig(WalletConfig delegate, NetworkParameters parameters, int socks5DiscoverMode,
public XmrNetworkConfig(WalletConfig delegate, NetworkParameters parameters, int socks5DiscoverMode,
@Nullable Socks5Proxy proxy) {
this.delegate = delegate;
this.parameters = parameters;

View file

@ -18,7 +18,7 @@
package haveno.core.xmr.nodes;
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
import haveno.core.xmr.nodes.BtcNodes.BtcNode;
import haveno.core.xmr.nodes.XmrNodes.XmrNode;
import haveno.network.DnsLookupException;
import haveno.network.DnsLookupTor;
import org.bitcoinj.core.PeerAddress;
@ -32,28 +32,28 @@ import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Objects;
class BtcNodeConverter {
private static final Logger log = LoggerFactory.getLogger(BtcNodeConverter.class);
class XmrNodeConverter {
private static final Logger log = LoggerFactory.getLogger(XmrNodeConverter.class);
private final Facade facade;
BtcNodeConverter() {
XmrNodeConverter() {
this.facade = new Facade();
}
BtcNodeConverter(Facade facade) {
XmrNodeConverter(Facade facade) {
this.facade = facade;
}
@Nullable
PeerAddress convertOnionHost(BtcNode node) {
PeerAddress convertOnionHost(XmrNode node) {
// no DNS lookup for onion addresses
String onionAddress = Objects.requireNonNull(node.getOnionAddress());
return new PeerAddress(onionAddress, node.getPort());
}
@Nullable
PeerAddress convertClearNode(BtcNode node) {
PeerAddress convertClearNode(XmrNode node) {
int port = node.getPort();
PeerAddress result = create(node.getHostNameOrAddress(), port);
@ -69,7 +69,7 @@ class BtcNodeConverter {
}
@Nullable
PeerAddress convertWithTor(BtcNode node, Socks5Proxy proxy) {
PeerAddress convertWithTor(XmrNode node, Socks5Proxy proxy) {
int port = node.getPort();
PeerAddress result = create(proxy, node.getHostNameOrAddress(), port);

View file

@ -0,0 +1,192 @@
/*
* This file is part of Haveno.
*
* Haveno is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Haveno is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package haveno.core.xmr.nodes;
import haveno.common.config.Config;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument;
@Slf4j
public class XmrNodes {
// TODO: rename to XmrNodeType ?
public enum MoneroNodesOption {
PROVIDED,
CUSTOM,
PUBLIC
}
public List<XmrNode> selectPreferredNodes(XmrNodesSetupPreferences xmrNodesSetupPreferences) {
return xmrNodesSetupPreferences.selectPreferredNodes(this);
}
// TODO: always using null hostname
public List<XmrNode> getAllXmrNodes() {
switch (Config.baseCurrencyNetwork()) {
case XMR_LOCAL:
return Arrays.asList(
new XmrNode(MoneroNodesOption.PROVIDED, null, null, "127.0.0.1", 28081, 1, "@local")
);
case XMR_STAGENET:
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.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.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")
);
default:
throw new IllegalStateException("Unexpected base currency network: " + Config.baseCurrencyNetwork());
}
}
public List<XmrNode> getProvidedXmrNodes() {
return getXmrNodes(MoneroNodesOption.PROVIDED);
}
public List<XmrNode> getPublicXmrNodes() {
return getXmrNodes(MoneroNodesOption.PUBLIC);
}
private List<XmrNode> getXmrNodes(MoneroNodesOption type) {
List<XmrNode> nodes = new ArrayList<>();
for (XmrNode node : getAllXmrNodes()) if (node.getType() == type) nodes.add(node);
return nodes;
}
public static List<XmrNodes.XmrNode> toCustomXmrNodesList(Collection<String> nodes) {
return nodes.stream()
.filter(e -> !e.isEmpty())
.map(XmrNodes.XmrNode::fromFullAddress)
.collect(Collectors.toList());
}
@EqualsAndHashCode
@Getter
public static class XmrNode {
private static final int DEFAULT_PORT = Config.baseCurrencyNetworkParameters().getPort();
private final MoneroNodesOption type;
@Nullable
private final String onionAddress;
@Nullable
private final String hostName;
@Nullable
private final String operator; // null in case the user provides a list of custom btc nodes
@Nullable
private final String address; // IPv4 address
private int port = DEFAULT_PORT;
private int priority = 0;
/**
* @param fullAddress [IPv4 address:port or onion:port]
* @return XmrNode instance
*/
public static XmrNode fromFullAddress(String fullAddress) {
String[] parts = fullAddress.split("]");
checkArgument(parts.length > 0);
String host = "";
int port = DEFAULT_PORT;
if (parts[0].contains("[") && parts[0].contains(":")) {
// IPv6 address and optional port number
// address part delimited by square brackets e.g. [2a01:123:456:789::2]:8333
host = parts[0].replace("[", "").replace("]", "");
if (parts.length == 2)
port = Integer.parseInt(parts[1].replace(":", ""));
} else if (parts[0].contains(":") && !parts[0].contains(".")) {
// IPv6 address only; not delimited by square brackets
host = parts[0];
} else if (parts[0].contains(".")) {
// address and an optional port number
// e.g. 127.0.0.1:8333 or abcdef123xyz.onion:9999
parts = fullAddress.split(":");
checkArgument(parts.length > 0);
host = parts[0];
if (parts.length == 2)
port = Integer.parseInt(parts[1]);
}
checkArgument(host.length() > 0, "XmrNode address format not recognised");
return host.contains(".onion") ? new XmrNode(MoneroNodesOption.CUSTOM, null, host, null, port, null, null) : new XmrNode(MoneroNodesOption.CUSTOM, null, null, host, port, null, null);
}
public XmrNode(MoneroNodesOption type,
@Nullable String hostName,
@Nullable String onionAddress,
@Nullable String address,
int port,
Integer priority,
@Nullable String operator) {
this.type = type;
this.hostName = hostName;
this.onionAddress = onionAddress;
this.address = address;
this.port = port;
this.priority = priority == null ? 0 : priority;
this.operator = operator;
}
public boolean hasOnionAddress() {
return onionAddress != null;
}
public String getHostNameOrAddress() {
if (hostName != null)
return hostName;
else
return address;
}
public boolean hasClearNetAddress() {
return hostName != null || address != null;
}
@Override
public String toString() {
return "onionAddress='" + onionAddress + '\'' +
", hostName='" + hostName + '\'' +
", address='" + address + '\'' +
", port='" + port + '\'' +
", priority='" + priority + '\'' +
", operator='" + operator;
}
}
}

View file

@ -27,15 +27,15 @@ import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class BtcNodesRepository {
private final BtcNodeConverter converter;
private final List<BtcNodes.BtcNode> nodes;
public class XmrNodesRepository {
private final XmrNodeConverter converter;
private final List<XmrNodes.XmrNode> nodes;
public BtcNodesRepository(List<BtcNodes.BtcNode> nodes) {
this(new BtcNodeConverter(), nodes);
public XmrNodesRepository(List<XmrNodes.XmrNode> nodes) {
this(new XmrNodeConverter(), nodes);
}
public BtcNodesRepository(BtcNodeConverter converter, List<BtcNodes.BtcNode> nodes) {
public XmrNodesRepository(XmrNodeConverter converter, List<XmrNodes.XmrNode> nodes) {
this.converter = converter;
this.nodes = nodes;
}
@ -61,21 +61,21 @@ public class BtcNodesRepository {
private List<PeerAddress> getClearNodes() {
return nodes.stream()
.filter(BtcNodes.BtcNode::hasClearNetAddress)
.filter(XmrNodes.XmrNode::hasClearNetAddress)
.flatMap(node -> nullableAsStream(converter.convertClearNode(node)))
.collect(Collectors.toList());
}
private List<PeerAddress> getOnionHosts() {
return nodes.stream()
.filter(BtcNodes.BtcNode::hasOnionAddress)
.filter(XmrNodes.XmrNode::hasOnionAddress)
.flatMap(node -> nullableAsStream(converter.convertOnionHost(node)))
.collect(Collectors.toList());
}
private List<PeerAddress> getClearNodesBehindProxy(Socks5Proxy proxy) {
return nodes.stream()
.filter(BtcNodes.BtcNode::hasClearNetAddress)
.filter(XmrNodes.XmrNode::hasClearNetAddress)
.flatMap(node -> nullableAsStream(converter.convertWithTor(node, proxy)))
.collect(Collectors.toList());
}

View file

@ -23,42 +23,41 @@ import haveno.core.user.Preferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.List;
import java.util.Set;
public class BtcNodesSetupPreferences {
private static final Logger log = LoggerFactory.getLogger(BtcNodesSetupPreferences.class);
public class XmrNodesSetupPreferences {
private static final Logger log = LoggerFactory.getLogger(XmrNodesSetupPreferences.class);
private final Preferences preferences;
public BtcNodesSetupPreferences(Preferences preferences) {
public XmrNodesSetupPreferences(Preferences preferences) {
this.preferences = preferences;
}
public List<BtcNodes.BtcNode> selectPreferredNodes(BtcNodes nodes) {
List<BtcNodes.BtcNode> result;
public List<XmrNodes.XmrNode> selectPreferredNodes(XmrNodes nodes) {
List<XmrNodes.XmrNode> result;
BtcNodes.BitcoinNodesOption nodesOption = BtcNodes.BitcoinNodesOption.values()[preferences.getBitcoinNodesOptionOrdinal()];
XmrNodes.MoneroNodesOption nodesOption = XmrNodes.MoneroNodesOption.values()[preferences.getMoneroNodesOptionOrdinal()];
switch (nodesOption) {
case CUSTOM:
String bitcoinNodes = preferences.getBitcoinNodes();
String bitcoinNodes = preferences.getMoneroNodes();
Set<String> distinctNodes = Utilities.commaSeparatedListToSet(bitcoinNodes, false);
result = BtcNodes.toBtcNodesList(distinctNodes);
result = XmrNodes.toCustomXmrNodesList(distinctNodes);
if (result.isEmpty()) {
log.warn("Custom nodes is set but no valid nodes are provided. " +
"We fall back to provided nodes option.");
preferences.setBitcoinNodesOptionOrdinal(BtcNodes.BitcoinNodesOption.PROVIDED.ordinal());
result = nodes.getProvidedBtcNodes();
preferences.setMoneroNodesOptionOrdinal(XmrNodes.MoneroNodesOption.PROVIDED.ordinal());
result = nodes.getAllXmrNodes();
}
break;
case PUBLIC:
result = Collections.emptyList();
result = nodes.getPublicXmrNodes();
break;
case PROVIDED:
default:
result = nodes.getProvidedBtcNodes();
result = nodes.getAllXmrNodes();
break;
}
@ -66,11 +65,11 @@ public class BtcNodesSetupPreferences {
}
public boolean isUseCustomNodes() {
return BtcNodes.BitcoinNodesOption.CUSTOM.ordinal() == preferences.getBitcoinNodesOptionOrdinal();
return XmrNodes.MoneroNodesOption.CUSTOM.ordinal() == preferences.getMoneroNodesOptionOrdinal();
}
public int calculateMinBroadcastConnections(List<BtcNodes.BtcNode> nodes) {
BtcNodes.BitcoinNodesOption nodesOption = BtcNodes.BitcoinNodesOption.values()[preferences.getBitcoinNodesOptionOrdinal()];
public int calculateMinBroadcastConnections(List<XmrNodes.XmrNode> nodes) {
XmrNodes.MoneroNodesOption nodesOption = XmrNodes.MoneroNodesOption.values()[preferences.getMoneroNodesOptionOrdinal()];
int result;
switch (nodesOption) {
case CUSTOM:

View file

@ -33,11 +33,11 @@ import haveno.core.user.Preferences;
import haveno.core.xmr.exceptions.InvalidHostException;
import haveno.core.xmr.model.AddressEntry;
import haveno.core.xmr.model.AddressEntryList;
import haveno.core.xmr.nodes.BtcNetworkConfig;
import haveno.core.xmr.nodes.BtcNodes;
import haveno.core.xmr.nodes.BtcNodes.BtcNode;
import haveno.core.xmr.nodes.BtcNodesRepository;
import haveno.core.xmr.nodes.BtcNodesSetupPreferences;
import haveno.core.xmr.nodes.XmrNetworkConfig;
import haveno.core.xmr.nodes.XmrNodes;
import haveno.core.xmr.nodes.XmrNodes.XmrNode;
import haveno.core.xmr.nodes.XmrNodesRepository;
import haveno.core.xmr.nodes.XmrNodesSetupPreferences;
import haveno.core.xmr.nodes.LocalBitcoinNode;
import haveno.network.Socks5MultiDiscovery;
import haveno.network.Socks5ProxyProvider;
@ -101,7 +101,7 @@ public class WalletsSetup {
private final Socks5ProxyProvider socks5ProxyProvider;
private final Config config;
private final LocalBitcoinNode localBitcoinNode;
private final BtcNodes btcNodes;
private final XmrNodes xmrNodes;
private final int numConnectionsForBtc;
private final String userAgent;
private final NetworkParameters params;
@ -127,7 +127,7 @@ public class WalletsSetup {
Socks5ProxyProvider socks5ProxyProvider,
Config config,
LocalBitcoinNode localBitcoinNode,
BtcNodes btcNodes,
XmrNodes xmrNodes,
@Named(Config.USER_AGENT) String userAgent,
@Named(Config.WALLET_DIR) File walletDir,
@Named(Config.USE_ALL_PROVIDED_NODES) boolean useAllProvidedNodes,
@ -139,7 +139,7 @@ public class WalletsSetup {
this.socks5ProxyProvider = socks5ProxyProvider;
this.config = config;
this.localBitcoinNode = localBitcoinNode;
this.btcNodes = btcNodes;
this.xmrNodes = xmrNodes;
this.numConnectionsForBtc = numConnectionsForBtc;
this.useAllProvidedNodes = useAllProvidedNodes;
this.userAgent = userAgent;
@ -171,7 +171,7 @@ public class WalletsSetup {
backupWallets();
final Socks5Proxy socks5Proxy = preferences.getUseTorForBitcoinJ() ? socks5ProxyProvider.getSocks5Proxy() : null;
final Socks5Proxy socks5Proxy = preferences.getUseTorForMonero() ? socks5ProxyProvider.getSocks5Proxy() : null;
log.info("Socks5Proxy for bitcoinj: socks5Proxy=" + socks5Proxy);
walletConfig = new WalletConfig(params, walletDir, "haveno") {
@ -195,7 +195,7 @@ public class WalletsSetup {
};
walletConfig.setSocks5Proxy(socks5Proxy);
walletConfig.setConfig(config);
walletConfig.setLocalBitcoinNode(localBitcoinNode);
walletConfig.setLocalBitcoinNode(localBitcoinNode); // TODO: adapt to xmr or remove
walletConfig.setUserAgent(userAgent, Version.VERSION);
walletConfig.setNumConnectionsForBtc(numConnectionsForBtc);
@ -213,7 +213,7 @@ public class WalletsSetup {
walletConfig.setCheckpoints(getClass().getResourceAsStream(checkpointsPath));
}
// TODO: update this for xmr
if (params.getId().equals(NetworkParameters.ID_REGTEST)) {
walletConfig.setMinBroadcastConnections(1);
if (regTestHost == RegTestHost.LOCALHOST) {
@ -235,7 +235,7 @@ public class WalletsSetup {
walletConfig.connectToLocalHost();
} else {
try {
configPeerNodes(socks5Proxy);
//configPeerNodes(socks5Proxy);
} catch (IllegalArgumentException e) {
timeoutTimer.stop();
walletsSetupFailed.set(true);
@ -329,14 +329,14 @@ public class WalletsSetup {
private void configPeerNodes(@Nullable Socks5Proxy proxy) {
walletConfig.setMinBroadcastConnections(MIN_BROADCAST_CONNECTIONS);
BtcNodesSetupPreferences btcNodesSetupPreferences = new BtcNodesSetupPreferences(preferences);
List<BtcNode> nodes = btcNodesSetupPreferences.selectPreferredNodes(btcNodes);
XmrNodesSetupPreferences xmrNodesSetupPreferences = new XmrNodesSetupPreferences(preferences);
List<XmrNode> nodes = xmrNodesSetupPreferences.selectPreferredNodes(xmrNodes);
BtcNodesRepository repository = new BtcNodesRepository(nodes);
boolean isUseClearNodesWithProxies = (useAllProvidedNodes || btcNodesSetupPreferences.isUseCustomNodes());
XmrNodesRepository repository = new XmrNodesRepository(nodes);
boolean isUseClearNodesWithProxies = (useAllProvidedNodes || xmrNodesSetupPreferences.isUseCustomNodes());
List<PeerAddress> peers = repository.getPeerAddresses(proxy, isUseClearNodesWithProxies);
BtcNetworkConfig networkConfig = new BtcNetworkConfig(walletConfig, params, socks5DiscoverMode, proxy);
XmrNetworkConfig networkConfig = new XmrNetworkConfig(walletConfig, params, socks5DiscoverMode, proxy);
networkConfig.proposePeers(peers);
}

View file

@ -626,6 +626,8 @@ public class XmrWalletService {
// sync wallet if open
if (wallet != null) {
log.info("Monero wallet uri={}, path={}", wallet.getRpcConnection().getUri(), wallet.getPath());
int numAttempts = 0;
int maxAttempts = 2;
while (!HavenoUtils.havenoSetup.getWalletInitialized().get()) {
try {
@ -648,8 +650,17 @@ public class XmrWalletService {
// save but skip backup on initialization
saveMainWallet(false);
} catch (Exception e) {
log.warn("Error syncing main wallet: {}. Trying again in {} seconds", e.getMessage(), connectionsService.getRefreshPeriodMs() / 1000);
GenUtils.waitFor(connectionsService.getRefreshPeriodMs());
log.warn("Error syncing main wallet: {}", e.getMessage());
numAttempts++;
if (numAttempts < maxAttempts) {
log.warn("Trying again in {} seconds", connectionsService.getRefreshPeriodMs() / 1000);
GenUtils.waitFor(connectionsService.getRefreshPeriodMs());
} else {
log.warn("Failed to sync main wallet after {} attempts. Opening app without syncing", maxAttempts);
HavenoUtils.havenoSetup.getWalletInitialized().set(true);
saveMainWallet(false);
break;
}
}
}
@ -735,9 +746,10 @@ public class XmrWalletService {
if (connection != null) {
cmd.add("--daemon-address");
cmd.add(connection.getUri());
if (connection.isOnion() && connection.getProxyUri() != null) {
if (connection.getProxyUri() != null) {
cmd.add("--proxy");
cmd.add(connection.getProxyUri());
if (!connection.isOnion()) cmd.add("--daemon-ssl-allow-any-cert"); // necessary to use proxy with clearnet mmonerod
}
if (connection.getUsername() != null) {
cmd.add("--daemon-login");
@ -1015,10 +1027,10 @@ public class XmrWalletService {
public List<MoneroTxWallet> getTxsWithIncomingOutputs(Integer subaddressIndex) {
List<MoneroTxWallet> txs = wallet.getTxs(new MoneroTxQuery().setIncludeOutputs(true));
return getTxsWithIncomingOutputs(txs, subaddressIndex);
return getTxsWithIncomingOutputs(subaddressIndex, txs);
}
public static List<MoneroTxWallet> getTxsWithIncomingOutputs(List<MoneroTxWallet> txs, Integer subaddressIndex) {
public static List<MoneroTxWallet> getTxsWithIncomingOutputs(Integer subaddressIndex, List<MoneroTxWallet> txs) {
List<MoneroTxWallet> incomingTxs = new ArrayList<>();
for (MoneroTxWallet tx : txs) {
boolean isIncoming = false;