From 0015bccf1d3d5bce72fecdb8e67d755a21359343 Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Thu, 11 Jul 2019 11:40:17 +0200 Subject: [PATCH 01/24] Gui mockup --- .../main/settings/network/NetworkSettingsView.fxml | 13 ++++++++++--- .../main/settings/network/NetworkSettingsView.java | 5 ++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.fxml b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.fxml index d583b18a099..414b73f6053 100644 --- a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.fxml +++ b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.fxml @@ -103,12 +103,19 @@ - + - + + + + + + + + diff --git a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java index 2c58462bce0..ce9516cb174 100644 --- a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java +++ b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java @@ -103,7 +103,7 @@ public class NetworkSettingsView extends ActivatableView { @FXML Label reSyncSPVChainLabel; @FXML - AutoTooltipButton reSyncSPVChainButton, openTorSettingsButton; + AutoTooltipButton reSyncSPVChainButton, renewIdButton, exportIdButton, importIdButton, openTorSettingsButton; private final Preferences preferences; private final BtcNodes btcNodes; @@ -177,6 +177,9 @@ public void initialize() { usePublicNodesRadio.setText(Res.get("settings.net.usePublicNodesRadio")); reSyncSPVChainLabel.setText(Res.get("settings.net.reSyncSPVChainLabel")); reSyncSPVChainButton.updateText(Res.get("settings.net.reSyncSPVChainButton")); + renewIdButton.updateText("Renew ID"); + exportIdButton.updateText("Export ID"); + importIdButton.updateText("Import ID"); p2PPeersLabel.setText(Res.get("settings.net.p2PPeersLabel")); onionAddressColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.onionAddressColumn"))); onionAddressColumn.getStyleClass().add("first-column"); From 8fb996d589b7a22902080a96e698469bc8b18831 Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Fri, 26 Jul 2019 12:12:32 +0200 Subject: [PATCH 02/24] Refactored Tor-related stuff Prepare for multiple hidden services --- .../network/p2p/network/TorNetworkNode.java | 89 ++++++++++--------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java index 6659c603640..2a7fdb02988 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java @@ -75,7 +75,6 @@ public class TorNetworkNode extends NetworkNode { private int restartCounter; @SuppressWarnings("FieldCanBeLocal") private MonadicBinding allShutDown; - private Tor tor; private TorMode torMode; @@ -107,8 +106,22 @@ public void start(@Nullable SetupListener setupListener) { if (setupListener != null) addSetupListener(setupListener); - // Create the tor node (takes about 6 sec.) - createTorAndHiddenService(Utils.findFreeSystemPort(), servicePort); + ListenableFuture future = executorService.submit(() -> { + // Create the tor node (takes about 6 sec.) + createTor(torMode); + + createHiddenService(Utils.findFreeSystemPort(), servicePort); + + return null; + }); + Futures.addCallback(future, new FutureCallback() { + public void onSuccess(Void ignore) { + } + + public void onFailure(@NotNull Throwable throwable) { + UserThread.execute(() -> log.error("Hidden service creation failed: " + throwable)); + } + }); } @Override @@ -131,7 +144,7 @@ public Socks5Proxy getSocksProxy() { } if (socksProxy == null || streamIsolation) { - tor = Tor.getDefault(); + Tor tor = Tor.getDefault(); // ask for the connection socksProxy = tor != null ? tor.getProxy(stream) : null; @@ -185,8 +198,8 @@ private BooleanProperty torNetworkNodeShutDown() { long ts = System.currentTimeMillis(); log.debug("Shutdown torNetworkNode"); try { - if (tor != null) - tor.shutdown(); + if (Tor.getDefault() != null) + Tor.getDefault().shutdown(); log.debug("Shutdown torNetworkNode done after " + (System.currentTimeMillis() - ts) + " ms."); } catch (Throwable e) { log.error("Shutdown torNetworkNode failed with exception: " + e.getMessage()); @@ -244,17 +257,41 @@ private void restartTor(String errorMessage) { // create tor /////////////////////////////////////////////////////////////////////////////////////////// - private void createTorAndHiddenService(int localPort, int servicePort) { - ListenableFuture future = executorService.submit(() -> { - try { - // get tor - Tor.setDefault(torMode.getTor()); + /** + * Attempt to create tor. Handles all exceptions and tries to restart Tor if necessary. + * + * @param torMode + */ + private void createTor(TorMode torMode) { + try { + Tor.setDefault(torMode.getTor()); + UserThread.execute(() -> setupListeners.forEach(SetupListener::onTorNodeReady)); + } catch (TorCtlException e) { + String msg = e.getCause() != null ? e.getCause().toString() : e.toString(); + log.error("Tor node creation failed: {}", msg); + if (e.getCause() instanceof IOException) { + // Since we cannot connect to Tor, we cannot do nothing. + // Furthermore, we have no hidden services started yet, so there is no graceful + // shutdown needed either + UserThread.execute(() -> setupListeners.forEach(s -> s.onSetupFailed(new RuntimeException(msg)))); + } else { + restartTor(e.getMessage()); + } + } catch (IOException e) { + log.error("Could not connect to running Tor: {}", e.getMessage()); + // Since we cannot connect to Tor, we cannot do nothing. + // Furthermore, we have no hidden services started yet, so there is no graceful + // shutdown needed either + UserThread.execute(() -> setupListeners.forEach(s -> s.onSetupFailed(new RuntimeException(e.getMessage())))); + } + } + private void createHiddenService(int localPort, int servicePort) { + try { // start hidden service long ts2 = new Date().getTime(); hiddenServiceSocket = new HiddenServiceSocket(localPort, torMode.getHiddenServiceDirectory(), servicePort); nodeAddressProperty.set(new NodeAddress(hiddenServiceSocket.getServiceName() + ":" + hiddenServiceSocket.getHiddenServicePort())); - UserThread.execute(() -> setupListeners.forEach(SetupListener::onTorNodeReady)); hiddenServiceSocket.addReadyListener(socket -> { try { log.info("\n################################################################\n" + @@ -265,7 +302,6 @@ private void createTorAndHiddenService(int localPort, int servicePort) { @Override public void run() { try { - nodeAddressProperty.set(new NodeAddress(hiddenServiceSocket.getServiceName() + ":" + hiddenServiceSocket.getHiddenServicePort())); startServer(socket); UserThread.execute(() -> setupListeners.forEach(SetupListener::onHiddenServicePublished)); } catch (final Exception e1) { @@ -281,35 +317,8 @@ public void run() { return null; }); log.info("It will take some time for the HS to be reachable (~40 seconds). You will be notified about this"); - } catch (TorCtlException e) { - String msg = e.getCause() != null ? e.getCause().toString() : e.toString(); - log.error("Tor node creation failed: {}", msg); - if (e.getCause() instanceof IOException) { - // Since we cannot connect to Tor, we cannot do nothing. - // Furthermore, we have no hidden services started yet, so there is no graceful - // shutdown needed either - UserThread.execute(() -> setupListeners.forEach(s -> s.onSetupFailed(new RuntimeException(msg)))); - } else { - restartTor(e.getMessage()); - } - } catch (IOException e) { - log.error("Could not connect to running Tor: {}", e.getMessage()); - // Since we cannot connect to Tor, we cannot do nothing. - // Furthermore, we have no hidden services started yet, so there is no graceful - // shutdown needed either - UserThread.execute(() -> setupListeners.forEach(s -> s.onSetupFailed(new RuntimeException(e.getMessage())))); } catch (Throwable ignore) { } - return null; - }); - Futures.addCallback(future, new FutureCallback() { - public void onSuccess(Void ignore) { - } - - public void onFailure(@NotNull Throwable throwable) { - UserThread.execute(() -> log.error("Hidden service creation failed: " + throwable)); - } - }); } } From 47d2c7a80c8c58d692a30fd57d3c1b7e13aab67f Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Fri, 26 Jul 2019 15:16:33 +0200 Subject: [PATCH 03/24] Migrate to new folder structure of HSs --- .../main/java/bisq/monitor/AvailableTor.java | 4 ++-- .../java/bisq/network/p2p/network/NewTor.java | 4 ++-- .../bisq/network/p2p/network/RunningTor.java | 4 ++-- .../bisq/network/p2p/network/TorMode.java | 4 ++-- .../network/p2p/network/TorNetworkNode.java | 22 ++++++++++++++----- 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/monitor/src/main/java/bisq/monitor/AvailableTor.java b/monitor/src/main/java/bisq/monitor/AvailableTor.java index 650425fca52..760068fd235 100644 --- a/monitor/src/main/java/bisq/monitor/AvailableTor.java +++ b/monitor/src/main/java/bisq/monitor/AvailableTor.java @@ -43,8 +43,8 @@ public Tor getTor() { } @Override - public String getHiddenServiceDirectory() { - return hiddenServiceDirectory; + public File getHiddenServiceBaseDirectory() { + return new File(hiddenServiceDirectory); } } diff --git a/p2p/src/main/java/bisq/network/p2p/network/NewTor.java b/p2p/src/main/java/bisq/network/p2p/network/NewTor.java index b94c3886028..9e84d72e659 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/NewTor.java +++ b/p2p/src/main/java/bisq/network/p2p/network/NewTor.java @@ -112,8 +112,8 @@ public Tor getTor() throws IOException, TorCtlException { } @Override - public String getHiddenServiceDirectory() { - return ""; + public File getHiddenServiceBaseDirectory() { + return new File(torDir, "hiddenservice"); } } diff --git a/p2p/src/main/java/bisq/network/p2p/network/RunningTor.java b/p2p/src/main/java/bisq/network/p2p/network/RunningTor.java index 4af31d2ab94..0de3956345f 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/RunningTor.java +++ b/p2p/src/main/java/bisq/network/p2p/network/RunningTor.java @@ -79,8 +79,8 @@ else if (cookieFile.exists()) } @Override - public String getHiddenServiceDirectory() { - return new File(torDir, HIDDEN_SERVICE_DIRECTORY).getAbsolutePath(); + public File getHiddenServiceBaseDirectory() { + return new File(torDir, HIDDEN_SERVICE_DIRECTORY); } } diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorMode.java b/p2p/src/main/java/bisq/network/p2p/network/TorMode.java index b1a8e88b823..26d2ee0c8b0 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorMode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorMode.java @@ -49,7 +49,7 @@ public abstract class TorMode { * sits in. Note that, due to the inner workings of the * Netlayer dependency, it does not * necessarily equal - * {@link TorMode#getHiddenServiceDirectory()}. + * {@link TorMode#getHiddenServiceBaseDirectory()}. */ public TorMode(File torDir) { this.torDir = torDir; @@ -77,7 +77,7 @@ public TorMode(File torDir) { * "torDir/externalTorHiddenService" in {@link RunningTor} * mode */ - public abstract String getHiddenServiceDirectory(); + public abstract File getHiddenServiceBaseDirectory(); /** * Do a rolling backup of the "private_key" file. diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java index 2a7fdb02988..f72616f5035 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java @@ -47,6 +47,7 @@ import java.net.Socket; +import java.io.File; import java.io.IOException; import java.util.Base64; @@ -101,8 +102,6 @@ public TorNetworkNode(int servicePort, NetworkProtoResolver networkProtoResolver @Override public void start(@Nullable SetupListener setupListener) { - torMode.doRollingBackup(); - if (setupListener != null) addSetupListener(setupListener); @@ -110,7 +109,19 @@ public void start(@Nullable SetupListener setupListener) { // Create the tor node (takes about 6 sec.) createTor(torMode); - createHiddenService(Utils.findFreeSystemPort(), servicePort); + // see if we have to migrate the old file structure + if(torMode.getHiddenServiceBaseDirectory().listFiles((dir, name) -> name.equals("hostname")).length > 0) { + File newHiddenServiceDirectory = new File(torMode.getHiddenServiceBaseDirectory(), "0"); + newHiddenServiceDirectory.mkdir(); + for(File current : torMode.getHiddenServiceBaseDirectory().listFiles()) + current.renameTo(new File(newHiddenServiceDirectory, current.getName())); + } + + // find hidden service candidates + File[] hiddenServiceDirs = torMode.getHiddenServiceBaseDirectory().listFiles(); + for(File current : hiddenServiceDirs) + if(current.isDirectory()) + createHiddenService(current.getName(), Utils.findFreeSystemPort(), servicePort); return null; }); @@ -286,11 +297,11 @@ private void createTor(TorMode torMode) { } } - private void createHiddenService(int localPort, int servicePort) { + private void createHiddenService(String hiddenServiceDirectory, int localPort, int servicePort) { try { // start hidden service long ts2 = new Date().getTime(); - hiddenServiceSocket = new HiddenServiceSocket(localPort, torMode.getHiddenServiceDirectory(), servicePort); + hiddenServiceSocket = new HiddenServiceSocket(localPort, hiddenServiceDirectory, servicePort); nodeAddressProperty.set(new NodeAddress(hiddenServiceSocket.getServiceName() + ":" + hiddenServiceSocket.getHiddenServicePort())); hiddenServiceSocket.addReadyListener(socket -> { try { @@ -319,6 +330,5 @@ public void run() { log.info("It will take some time for the HS to be reachable (~40 seconds). You will be notified about this"); } catch (Throwable ignore) { } - } } From 7a1e784392a9cf225c59b5575d0521fa3eb5eea5 Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Fri, 26 Jul 2019 15:30:01 +0200 Subject: [PATCH 04/24] Only report HSready when all HS are started --- .../bisq/network/p2p/network/TorNetworkNode.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java index f72616f5035..3ecc5ff6e5e 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java @@ -52,6 +52,7 @@ import java.util.Base64; import java.util.Date; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; @@ -119,9 +120,16 @@ public void start(@Nullable SetupListener setupListener) { // find hidden service candidates File[] hiddenServiceDirs = torMode.getHiddenServiceBaseDirectory().listFiles(); + + // start + CountDownLatch gate = new CountDownLatch(hiddenServiceDirs.length); for(File current : hiddenServiceDirs) if(current.isDirectory()) - createHiddenService(current.getName(), Utils.findFreeSystemPort(), servicePort); + createHiddenService(current.getName(), Utils.findFreeSystemPort(), servicePort, gate); + + // only report HiddenServicePublished once all are published + gate.await(90, TimeUnit.SECONDS); + UserThread.execute(() -> setupListeners.forEach(SetupListener::onHiddenServicePublished)); return null; }); @@ -297,7 +305,7 @@ private void createTor(TorMode torMode) { } } - private void createHiddenService(String hiddenServiceDirectory, int localPort, int servicePort) { + private void createHiddenService(String hiddenServiceDirectory, int localPort, int servicePort, CountDownLatch onHSReady) { try { // start hidden service long ts2 = new Date().getTime(); @@ -314,7 +322,6 @@ private void createHiddenService(String hiddenServiceDirectory, int localPort, i public void run() { try { startServer(socket); - UserThread.execute(() -> setupListeners.forEach(SetupListener::onHiddenServicePublished)); } catch (final Exception e1) { log.error(e1.toString()); e1.printStackTrace(); @@ -327,6 +334,7 @@ public void run() { } return null; }); + hiddenServiceSocket.addReadyListener(hiddenServiceSocket1 -> { onHSReady.countDown(); return null;} ); log.info("It will take some time for the HS to be reachable (~40 seconds). You will be notified about this"); } catch (Throwable ignore) { } From 92b6f9f7d278edbd6281c87865c231e65915939e Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Fri, 26 Jul 2019 15:43:55 +0200 Subject: [PATCH 05/24] Report TorNode ready once we now our address --- .../network/p2p/network/TorNetworkNode.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java index 3ecc5ff6e5e..0c66f2eefdf 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java @@ -50,7 +50,9 @@ import java.io.File; import java.io.IOException; +import java.util.Arrays; import java.util.Base64; +import java.util.Comparator; import java.util.Date; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -121,11 +123,19 @@ public void start(@Nullable SetupListener setupListener) { // find hidden service candidates File[] hiddenServiceDirs = torMode.getHiddenServiceBaseDirectory().listFiles(); + // sort in ascending order + Arrays.sort(hiddenServiceDirs, Comparator.comparing(File::getName)); + // start CountDownLatch gate = new CountDownLatch(hiddenServiceDirs.length); + NodeAddress nodeAddress = null; for(File current : hiddenServiceDirs) if(current.isDirectory()) - createHiddenService(current.getName(), Utils.findFreeSystemPort(), servicePort, gate); + nodeAddress = createHiddenService(current.getName(), Utils.findFreeSystemPort(), servicePort, gate); + + // use newest HS as for NodeAddress + nodeAddressProperty.set(nodeAddress); + UserThread.execute(() -> setupListeners.forEach(SetupListener::onTorNodeReady)); // only report HiddenServicePublished once all are published gate.await(90, TimeUnit.SECONDS); @@ -284,7 +294,6 @@ private void restartTor(String errorMessage) { private void createTor(TorMode torMode) { try { Tor.setDefault(torMode.getTor()); - UserThread.execute(() -> setupListeners.forEach(SetupListener::onTorNodeReady)); } catch (TorCtlException e) { String msg = e.getCause() != null ? e.getCause().toString() : e.toString(); log.error("Tor node creation failed: {}", msg); @@ -305,12 +314,11 @@ private void createTor(TorMode torMode) { } } - private void createHiddenService(String hiddenServiceDirectory, int localPort, int servicePort, CountDownLatch onHSReady) { - try { + private NodeAddress createHiddenService(String hiddenServiceDirectory, int localPort, int servicePort, CountDownLatch onHSReady) { // start hidden service long ts2 = new Date().getTime(); hiddenServiceSocket = new HiddenServiceSocket(localPort, hiddenServiceDirectory, servicePort); - nodeAddressProperty.set(new NodeAddress(hiddenServiceSocket.getServiceName() + ":" + hiddenServiceSocket.getHiddenServicePort())); + NodeAddress nodeAddress = new NodeAddress(hiddenServiceSocket.getServiceName() + ":" + hiddenServiceSocket.getHiddenServicePort()); hiddenServiceSocket.addReadyListener(socket -> { try { log.info("\n################################################################\n" + @@ -336,7 +344,6 @@ public void run() { }); hiddenServiceSocket.addReadyListener(hiddenServiceSocket1 -> { onHSReady.countDown(); return null;} ); log.info("It will take some time for the HS to be reachable (~40 seconds). You will be notified about this"); - } catch (Throwable ignore) { - } + return nodeAddress; } } From c2d9138a94a8ca184ee17cab2e07ae12bea22012 Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Fri, 26 Jul 2019 15:55:13 +0200 Subject: [PATCH 06/24] Formatting cosmetics --- .../network/p2p/network/TorNetworkNode.java | 68 ++++++++++--------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java index 0c66f2eefdf..ef57b13fda7 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java @@ -113,10 +113,10 @@ public void start(@Nullable SetupListener setupListener) { createTor(torMode); // see if we have to migrate the old file structure - if(torMode.getHiddenServiceBaseDirectory().listFiles((dir, name) -> name.equals("hostname")).length > 0) { + if (torMode.getHiddenServiceBaseDirectory().listFiles((dir, name) -> name.equals("hostname")).length > 0) { File newHiddenServiceDirectory = new File(torMode.getHiddenServiceBaseDirectory(), "0"); newHiddenServiceDirectory.mkdir(); - for(File current : torMode.getHiddenServiceBaseDirectory().listFiles()) + for (File current : torMode.getHiddenServiceBaseDirectory().listFiles()) current.renameTo(new File(newHiddenServiceDirectory, current.getName())); } @@ -129,8 +129,8 @@ public void start(@Nullable SetupListener setupListener) { // start CountDownLatch gate = new CountDownLatch(hiddenServiceDirs.length); NodeAddress nodeAddress = null; - for(File current : hiddenServiceDirs) - if(current.isDirectory()) + for (File current : hiddenServiceDirs) + if (current.isDirectory()) nodeAddress = createHiddenService(current.getName(), Utils.findFreeSystemPort(), servicePort, gate); // use newest HS as for NodeAddress @@ -315,35 +315,37 @@ private void createTor(TorMode torMode) { } private NodeAddress createHiddenService(String hiddenServiceDirectory, int localPort, int servicePort, CountDownLatch onHSReady) { - // start hidden service - long ts2 = new Date().getTime(); - hiddenServiceSocket = new HiddenServiceSocket(localPort, hiddenServiceDirectory, servicePort); - NodeAddress nodeAddress = new NodeAddress(hiddenServiceSocket.getServiceName() + ":" + hiddenServiceSocket.getHiddenServicePort()); - hiddenServiceSocket.addReadyListener(socket -> { - try { - log.info("\n################################################################\n" + - "Tor hidden service published after {} ms. Socked={}\n" + - "################################################################", - (new Date().getTime() - ts2), socket); //takes usually 30-40 sec - new Thread() { - @Override - public void run() { - try { - startServer(socket); - } catch (final Exception e1) { - log.error(e1.toString()); - e1.printStackTrace(); - } - } - }.start(); - } catch (final Exception e) { - log.error(e.toString()); - e.printStackTrace(); + long ts2 = new Date().getTime(); + hiddenServiceSocket = new HiddenServiceSocket(localPort, hiddenServiceDirectory, servicePort); + NodeAddress nodeAddress = new NodeAddress(hiddenServiceSocket.getServiceName() + ":" + hiddenServiceSocket.getHiddenServicePort()); + hiddenServiceSocket.addReadyListener(socket -> { + try { + log.info("\n################################################################\n" + + "Tor hidden service published after {} ms. Socked={}\n" + + "################################################################", + (new Date().getTime() - ts2), socket); //takes usually 30-40 sec + new Thread() { + @Override + public void run() { + try { + startServer(socket); + } catch (final Exception e1) { + log.error(e1.toString()); + e1.printStackTrace(); + } } - return null; - }); - hiddenServiceSocket.addReadyListener(hiddenServiceSocket1 -> { onHSReady.countDown(); return null;} ); - log.info("It will take some time for the HS to be reachable (~40 seconds). You will be notified about this"); - return nodeAddress; + }.start(); + } catch (final Exception e) { + log.error(e.toString()); + e.printStackTrace(); + } + return null; + }); + hiddenServiceSocket.addReadyListener(hiddenServiceSocket1 -> { + onHSReady.countDown(); + return null; + }); + log.info("It will take some time for the HS to be reachable (~40 seconds). You will be notified about this"); + return nodeAddress; } } From 025f4cc1b7bb03d279762c500231b74a255995a3 Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Sat, 27 Jul 2019 14:23:52 +0200 Subject: [PATCH 07/24] Renew address is active --- .../settings/network/NetworkSettingsView.java | 7 ++++++ .../java/bisq/network/p2p/P2PService.java | 4 ++++ .../p2p/network/LocalhostNetworkNode.java | 5 ++++ .../bisq/network/p2p/network/NetworkNode.java | 2 ++ .../network/p2p/network/TorNetworkNode.java | 24 +++++++++++++++++++ 5 files changed, 42 insertions(+) diff --git a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java index ce9516cb174..571dcc0c90c 100644 --- a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java +++ b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java @@ -291,6 +291,11 @@ public void activate() { reSyncSPVChainButton.setOnAction(event -> GUIUtil.reSyncSPVChain(preferences)); + renewIdButton.setOnAction(event -> { + p2PService.renewHiddenService(); + showShutDownPopup(); + }); + bitcoinPeersSubscription = EasyBind.subscribe(walletsSetup.connectedPeersProperty(), connectedPeers -> updateBitcoinPeersTable()); @@ -333,6 +338,8 @@ public void deactivate() { useTorForBtcJCheckBox.setOnAction(null); + renewIdButton.setOnAction(null); + if (nodeAddressSubscription != null) nodeAddressSubscription.unsubscribe(); diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index d90fdeb2565..f8af5fc2281 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -204,6 +204,10 @@ public void onAllServicesInitialized() { } } + public void renewHiddenService() { + networkNode.renewHiddenService(); + } + public void shutDown(Runnable shutDownCompleteHandler) { if (!shutDownInProgress) { shutDownInProgress = true; diff --git a/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java index b49bff1e737..d24b216f1ba 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java @@ -84,6 +84,11 @@ public void start(@Nullable SetupListener setupListener) { }, simulateTorDelayHiddenService, TimeUnit.MILLISECONDS); } + @Override + public void renewHiddenService() { + + } + // Called from NetworkNode thread @Override protected Socket createSocket(NodeAddress peerNodeAddress) throws IOException { diff --git a/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java index 82b30c46ef4..a919325379f 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java @@ -231,6 +231,8 @@ public void onFailure(@NotNull Throwable throwable) { } } + public abstract void renewHiddenService(); + @Nullable private InboundConnection getInboundConnection(@NotNull NodeAddress peersNodeAddress) { Optional inboundConnectionOptional = lookupInBoundConnection(peersNodeAddress); diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java index ef57b13fda7..ce6b5f0a8f5 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java @@ -45,6 +45,8 @@ import java.security.SecureRandom; +import java.text.SimpleDateFormat; + import java.net.Socket; import java.io.File; @@ -103,6 +105,24 @@ public TorNetworkNode(int servicePort, NetworkProtoResolver networkProtoResolver // API /////////////////////////////////////////////////////////////////////////////////////////// + /** + * only prepares a fresh hidden service folder. The actual HS is only established and + * started on Bisq restart! + */ + @Override + public void renewHiddenService() { + // find suitable folder name + int seed = 0; + File newDir = null; + do { + String newHiddenServiceDirectory = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + seed; + newDir = new File(torMode.getHiddenServiceBaseDirectory(), newHiddenServiceDirectory); + seed += 1; + } while (newDir.exists()); + + newDir.mkdirs(); + } + @Override public void start(@Nullable SetupListener setupListener) { if (setupListener != null) @@ -112,6 +132,10 @@ public void start(@Nullable SetupListener setupListener) { // Create the tor node (takes about 6 sec.) createTor(torMode); + // check if we start our client for the first time + if (!torMode.getHiddenServiceBaseDirectory().exists()) + renewHiddenService(); + // see if we have to migrate the old file structure if (torMode.getHiddenServiceBaseDirectory().listFiles((dir, name) -> name.equals("hostname")).length > 0) { File newHiddenServiceDirectory = new File(torMode.getHiddenServiceBaseDirectory(), "0"); From 4929f06079e9037555f08dbbeda08ca6c3376469 Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Fri, 26 Jul 2019 13:57:51 +0200 Subject: [PATCH 08/24] Clear unused hidden services On trade complete and app shutdown, we check for hidden services that are still in active trades. We shut down all unused hidden services. --- .../java/bisq/core/trade/TradeManager.java | 18 ++++++++++++ .../java/bisq/network/p2p/P2PService.java | 22 ++++++++++++++ .../p2p/network/LocalhostNetworkNode.java | 6 ++++ .../bisq/network/p2p/network/NetworkNode.java | 8 +++++ .../network/p2p/network/TorNetworkNode.java | 29 ++++++++++++++++++- 5 files changed, 82 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/trade/TradeManager.java b/core/src/main/java/bisq/core/trade/TradeManager.java index 6cfbf7d5690..4ac06048ea7 100644 --- a/core/src/main/java/bisq/core/trade/TradeManager.java +++ b/core/src/main/java/bisq/core/trade/TradeManager.java @@ -88,7 +88,9 @@ import java.util.ArrayList; import java.util.Date; +import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.UUID; @@ -265,6 +267,7 @@ public void onUpdatedDataReceived() { } public void shutDown() { + reportActiveHiddenServices(); } private void initPendingTrades() { @@ -584,6 +587,21 @@ public void removePreparedTrade(Trade trade) { private void removeTrade(Trade trade) { tradableList.remove(trade); + + reportActiveHiddenServices(); + } + + /** + * Collect all node addresses (hidden services) we still need for active offers/trades + * and report them to the P2P Service. The P2PService will report to the NetworkNode + * for cleaning up unnecessary hidden services. + */ + private void reportActiveHiddenServices() { + Set result = new HashSet<>(); + result.addAll(openOfferManager.getObservableList().stream().map(openOffer -> openOffer.getOffer().getOfferPayload().getOwnerNodeAddress()).collect(Collectors.toSet())); + result.addAll(getTradableList().stream().map(Trade::getContract).filter(Objects::nonNull).map(contract -> contract.getMyNodeAddress(keyRing.getPubKeyRing())).collect(Collectors.toSet())); + + p2PService.reportRequiredHiddenServices(result); } diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index f8af5fc2281..444da379745 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -916,4 +916,26 @@ private boolean verifyAddressPrefixHash(PrefixedSealedAndSignedMessage prefixedS return false; } } + + /** + * Reporting hidden services that are still needed has the following background: + *

    + *
  • when we only report a hidden service if it is no longer used, we have that + * one chance of shutting it down. If for whatever reason we fail on doing so, we + * are stuck with this particular HS forever (without additional code) + *
  • we do not need to find our exact node address by complex queries to the trade + * and offers, we just add all node address to the list. If we report + * a HS that is not ours, well, then we cannot retain it as we do not have it + * and that is it. No harm done. On the other hand, if we select the wrong HS as + * being ours, we probably remove a HS that is still in use. + *
  • the code is much more compact and therefore, less error prone + *
  • by reporting the stuff we need to keep, our code is more robust in terms of + * missing one + *

+ * + * @param nodeAddressList the list of node addresses we need to retain + */ + public void reportRequiredHiddenServices(Set nodeAddressList) { + networkNode.clearHiddenServices(nodeAddressList); + } } diff --git a/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java index d24b216f1ba..89e73fcff8c 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java @@ -27,6 +27,7 @@ import java.io.IOException; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; @@ -94,4 +95,9 @@ public void renewHiddenService() { protected Socket createSocket(NodeAddress peerNodeAddress) throws IOException { return new Socket(peerNodeAddress.getHostName(), peerNodeAddress.getPort()); } + + @Override + public void clearHiddenServices(Set nodeAddressList) { + + } } diff --git a/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java index a919325379f..4bec0ae8312 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java @@ -233,6 +233,14 @@ public void onFailure(@NotNull Throwable throwable) { public abstract void renewHiddenService(); + /** + * Gets rid of all hidden services except those given in the retain + * parameter. + * + * @param retain contains the hidden services that are still in use + */ + public abstract void clearHiddenServices(Set retain); + @Nullable private InboundConnection getInboundConnection(@NotNull NodeAddress peersNodeAddress) { Optional inboundConnectionOptional = lookupInBoundConnection(peersNodeAddress); diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java index ce6b5f0a8f5..c961110391b 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java @@ -56,7 +56,10 @@ import java.util.Base64; import java.util.Comparator; import java.util.Date; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.CountDownLatch; +import java.util.Set; import java.util.concurrent.TimeUnit; import org.slf4j.Logger; @@ -88,6 +91,8 @@ public class TorNetworkNode extends NetworkNode { private Socks5Proxy socksProxy; + private Map nodeAddressToHSDirectory = new HashMap<>(); + /////////////////////////////////////////////////////////////////////////////////////////// // Constructor /////////////////////////////////////////////////////////////////////////////////////////// @@ -123,6 +128,25 @@ public void renewHiddenService() { newDir.mkdirs(); } + /** + * only marks the folders of the hidden services that are not to be retained for deletion. + * Once the application shuts down, the folders are deleted so that on app restart, + * the unnecessary hidden services are gone. + */ + @Override + public void clearHiddenServices(Set retain) { + // first and foremost, we always retain the newest HS. + retain.add(nodeAddressProperty.getValue()); + + // then, we clean the hidden service directory accordingly + // so they are gone after an app restart + nodeAddressToHSDirectory.entrySet().stream().filter(nodeAddressFileEntry -> !retain.contains(nodeAddressFileEntry.getKey())) + .forEach(nodeAddressFileEntry -> { + nodeAddressFileEntry.getValue().deleteOnExit(); + Arrays.stream(nodeAddressFileEntry.getValue().listFiles()).forEach(file -> file.deleteOnExit()); + }); + } + @Override public void start(@Nullable SetupListener setupListener) { if (setupListener != null) @@ -152,10 +176,13 @@ public void start(@Nullable SetupListener setupListener) { // start CountDownLatch gate = new CountDownLatch(hiddenServiceDirs.length); + nodeAddressToHSDirectory.clear(); NodeAddress nodeAddress = null; for (File current : hiddenServiceDirs) - if (current.isDirectory()) + if (current.isDirectory()) { nodeAddress = createHiddenService(current.getName(), Utils.findFreeSystemPort(), servicePort, gate); + nodeAddressToHSDirectory.put(nodeAddress, current); + } // use newest HS as for NodeAddress nodeAddressProperty.set(nodeAddress); From ff999278bc65d3ab12afd37f12c8313a52ebdc87 Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Tue, 30 Jul 2019 20:15:32 +0200 Subject: [PATCH 09/24] Reenable rolling backup --- .../network/p2p/network/TorNetworkNode.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java index c961110391b..e8640b12105 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java @@ -23,6 +23,7 @@ import bisq.common.Timer; import bisq.common.UserThread; import bisq.common.proto.network.NetworkProtoResolver; +import bisq.common.storage.FileUtil; import bisq.common.util.Utilities; import org.berndpruenster.netlayer.tor.HiddenServiceSocket; @@ -49,6 +50,9 @@ import java.net.Socket; +import java.nio.file.Files; +import java.nio.file.Path; + import java.io.File; import java.io.IOException; @@ -142,8 +146,14 @@ public void clearHiddenServices(Set retain) { // so they are gone after an app restart nodeAddressToHSDirectory.entrySet().stream().filter(nodeAddressFileEntry -> !retain.contains(nodeAddressFileEntry.getKey())) .forEach(nodeAddressFileEntry -> { - nodeAddressFileEntry.getValue().deleteOnExit(); - Arrays.stream(nodeAddressFileEntry.getValue().listFiles()).forEach(file -> file.deleteOnExit()); + try { + Files.walk(nodeAddressFileEntry.getValue().toPath()) + .sorted() + .map(Path::toFile) + .forEach(File::deleteOnExit); + } catch (IOException e) { + log.error("Error while trying to delete deprecated hidden service directory", e); + } }); } @@ -181,6 +191,9 @@ public void start(@Nullable SetupListener setupListener) { for (File current : hiddenServiceDirs) if (current.isDirectory()) { nodeAddress = createHiddenService(current.getName(), Utils.findFreeSystemPort(), servicePort, gate); + + FileUtil.rollingBackup(current, "private_key", 20); + nodeAddressToHSDirectory.put(nodeAddress, current); } From 3a3362cfb911690ed031dd64e2301e2b782edd76 Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Thu, 1 Aug 2019 10:54:16 +0200 Subject: [PATCH 10/24] Add informative popup before renew --- .../main/resources/i18n/displayStrings.properties | 2 ++ .../main/settings/network/NetworkSettingsView.java | 12 +++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 405932fbaf3..e17fbf0d22d 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -1073,6 +1073,8 @@ setting.preferences.dao.fullNodeInfo.cancel=No, I stick with lite node mode settings.net.btcHeader=Bitcoin network settings.net.p2pHeader=Bisq network settings.net.onionAddressLabel=My onion address +settings.net.renewAddressButton=Renew address +settings.net.renewAddress=Please be aware that you will loose your reputation when you renew your onion address. Furthermore, you need to restart the application to set your new onion address active.\n\nProceed? settings.net.btcNodesLabel=Use custom Bitcoin Core nodes settings.net.bitcoinPeersLabel=Connected peers settings.net.useTorForBtcJLabel=Use Tor for Bitcoin network diff --git a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java index 571dcc0c90c..630b7b27343 100644 --- a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java +++ b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java @@ -177,7 +177,7 @@ public void initialize() { usePublicNodesRadio.setText(Res.get("settings.net.usePublicNodesRadio")); reSyncSPVChainLabel.setText(Res.get("settings.net.reSyncSPVChainLabel")); reSyncSPVChainButton.updateText(Res.get("settings.net.reSyncSPVChainButton")); - renewIdButton.updateText("Renew ID"); + renewIdButton.updateText(Res.get("settings.net.renewAddressButton")); exportIdButton.updateText("Export ID"); importIdButton.updateText("Import ID"); p2PPeersLabel.setText(Res.get("settings.net.p2PPeersLabel")); @@ -292,8 +292,14 @@ public void activate() { reSyncSPVChainButton.setOnAction(event -> GUIUtil.reSyncSPVChain(preferences)); renewIdButton.setOnAction(event -> { - p2PService.renewHiddenService(); - showShutDownPopup(); + new Popup<>().information(Res.get("settings.net.renewAddress")) + .actionButtonText(Res.get("shared.applyAndShutDown")) + .onAction(() -> { + p2PService.renewHiddenService(); + UserThread.runAfter(BisqApp.getShutDownHandler()::run, 500, TimeUnit.MILLISECONDS); + }) + .closeButtonText(Res.get("shared.cancel")) + .show(); }); bitcoinPeersSubscription = EasyBind.subscribe(walletsSetup.connectedPeersProperty(), From e07af3462df56c0ac52d5d486c0767cfa53a8bce Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Wed, 31 Jul 2019 16:00:57 +0200 Subject: [PATCH 11/24] HS backup active --- .../resources/i18n/displayStrings.properties | 3 ++ .../settings/network/NetworkSettingsView.java | 16 ++++++- .../java/bisq/network/p2p/P2PService.java | 6 +++ .../p2p/network/LocalhostNetworkNode.java | 6 +++ .../bisq/network/p2p/network/NetworkNode.java | 3 ++ .../network/p2p/network/TorNetworkNode.java | 44 +++++++++++++++++++ 6 files changed, 77 insertions(+), 1 deletion(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index e17fbf0d22d..ace4879f93f 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -1075,6 +1075,9 @@ settings.net.p2pHeader=Bisq network settings.net.onionAddressLabel=My onion address settings.net.renewAddressButton=Renew address settings.net.renewAddress=Please be aware that you will loose your reputation when you renew your onion address. Furthermore, you need to restart the application to set your new onion address active.\n\nProceed? +settings.net.exportAddressButton=Backup address +settings.net.exportAddressFileDialog=Select the backup file +settings.net.exportAddressFileEnding=Bisq Onion Address Backup (*.bisq) settings.net.btcNodesLabel=Use custom Bitcoin Core nodes settings.net.bitcoinPeersLabel=Connected peers settings.net.useTorForBtcJLabel=Use Tor for Bitcoin network diff --git a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java index 630b7b27343..7271458698f 100644 --- a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java +++ b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java @@ -48,6 +48,8 @@ import javafx.fxml.FXML; +import javafx.stage.FileChooser; + import javafx.scene.control.CheckBox; import javafx.scene.control.Label; import javafx.scene.control.RadioButton; @@ -70,6 +72,8 @@ import javafx.collections.ObservableList; import javafx.collections.transformation.SortedList; +import java.io.File; + import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -178,7 +182,7 @@ public void initialize() { reSyncSPVChainLabel.setText(Res.get("settings.net.reSyncSPVChainLabel")); reSyncSPVChainButton.updateText(Res.get("settings.net.reSyncSPVChainButton")); renewIdButton.updateText(Res.get("settings.net.renewAddressButton")); - exportIdButton.updateText("Export ID"); + exportIdButton.updateText(Res.get("settings.net.exportAddressButton")); importIdButton.updateText("Import ID"); p2PPeersLabel.setText(Res.get("settings.net.p2PPeersLabel")); onionAddressColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.onionAddressColumn"))); @@ -302,6 +306,15 @@ public void activate() { .show(); }); + exportIdButton.setOnAction(event -> { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle(Res.get("settings.net.exportAddressFileDialog")); + fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Res.get("settings.net.exportAddressFileEnding"), "*.bisq")); + File file = fileChooser.showSaveDialog(root.getScene().getWindow()); + if (file != null) + p2PService.exportHiddenService(file); + }); + bitcoinPeersSubscription = EasyBind.subscribe(walletsSetup.connectedPeersProperty(), connectedPeers -> updateBitcoinPeersTable()); @@ -345,6 +358,7 @@ public void deactivate() { useTorForBtcJCheckBox.setOnAction(null); renewIdButton.setOnAction(null); + exportIdButton.setOnAction(null); if (nodeAddressSubscription != null) nodeAddressSubscription.unsubscribe(); diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index 444da379745..6bf93c9d76d 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -74,6 +74,8 @@ import java.security.PublicKey; +import java.io.File; + import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -938,4 +940,8 @@ private boolean verifyAddressPrefixHash(PrefixedSealedAndSignedMessage prefixedS public void reportRequiredHiddenServices(Set nodeAddressList) { networkNode.clearHiddenServices(nodeAddressList); } + + public void exportHiddenService(File file) { + networkNode.exportHiddenService(file); + } } diff --git a/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java index 89e73fcff8c..8d809abc633 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java @@ -25,6 +25,7 @@ import java.net.ServerSocket; import java.net.Socket; +import java.io.File; import java.io.IOException; import java.util.Set; @@ -100,4 +101,9 @@ protected Socket createSocket(NodeAddress peerNodeAddress) throws IOException { public void clearHiddenServices(Set nodeAddressList) { } + + @Override + public void exportHiddenService(File file) { + + } } diff --git a/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java index 4bec0ae8312..afb75746c65 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java @@ -40,6 +40,7 @@ import java.net.ServerSocket; import java.net.Socket; +import java.io.File; import java.io.IOException; import java.util.HashSet; @@ -241,6 +242,8 @@ public void onFailure(@NotNull Throwable throwable) { */ public abstract void clearHiddenServices(Set retain); + public abstract void exportHiddenService(File target); + @Nullable private InboundConnection getInboundConnection(@NotNull NodeAddress peersNodeAddress) { Optional inboundConnectionOptional = lookupInBoundConnection(peersNodeAddress); diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java index e8640b12105..2ea46ff00e6 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java @@ -54,6 +54,8 @@ import java.nio.file.Path; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.util.Arrays; @@ -65,6 +67,8 @@ import java.util.concurrent.CountDownLatch; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -157,6 +161,46 @@ public void clearHiddenServices(Set retain) { }); } + @Override + public void exportHiddenService(File target) { + // is directory? + if (target.isDirectory()) + target = new File(target, nodeAddressProperty.getValue().getHostName()); + + if (!target.getName().endsWith(".bisq")) + target = new File(target.getAbsolutePath() + ".bisq"); + + // create zip + File hiddenServiceDir = nodeAddressToHSDirectory.get(nodeAddressProperty.getValue()); + // write to file + try { + FileOutputStream fos = new FileOutputStream(target); + ZipOutputStream zipOut = new ZipOutputStream(fos); + + Arrays.stream(hiddenServiceDir.listFiles()).filter(file -> !file.isDirectory()) + .forEach(file -> { + try { + zipOut.putNextEntry(new ZipEntry(file.getName())); + + FileInputStream fis = new FileInputStream(file); + byte[] bytes = new byte[1024]; + int length; + while ((length = fis.read(bytes)) >= 0) { + zipOut.write(bytes, 0, length); + } + fis.close(); + } catch (IOException e) { + log.error("Error while exporting hidden service.", e); + } + } + ); + zipOut.close(); + fos.close(); + } catch (IOException | NullPointerException e) { + log.error("Error while exporting hidden service.", e); + } + } + @Override public void start(@Nullable SetupListener setupListener) { if (setupListener != null) From b34ad56c0422e323453354de3a9c100a291dafee Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Thu, 1 Aug 2019 10:46:20 +0200 Subject: [PATCH 12/24] HS restore active --- .../resources/i18n/displayStrings.properties | 8 +++- .../settings/network/NetworkSettingsView.java | 25 +++++++++++- .../java/bisq/network/p2p/P2PService.java | 5 +++ .../p2p/network/LocalhostNetworkNode.java | 9 ++++- .../bisq/network/p2p/network/NetworkNode.java | 4 +- .../network/p2p/network/TorNetworkNode.java | 38 ++++++++++++++++++- 6 files changed, 82 insertions(+), 7 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index ace4879f93f..fbcc4783cdf 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -1073,11 +1073,15 @@ setting.preferences.dao.fullNodeInfo.cancel=No, I stick with lite node mode settings.net.btcHeader=Bitcoin network settings.net.p2pHeader=Bisq network settings.net.onionAddressLabel=My onion address -settings.net.renewAddressButton=Renew address -settings.net.renewAddress=Please be aware that you will loose your reputation when you renew your onion address. Furthermore, you need to restart the application to set your new onion address active.\n\nProceed? +settings.net.renewAddressButton=Create new address +settings.net.renewAddress=Please be aware that you will loose your reputation when you create and use a new onion address. Furthermore, you need to restart the application to set your new onion address active.\n\nProceed? settings.net.exportAddressButton=Backup address settings.net.exportAddressFileDialog=Select the backup file settings.net.exportAddressFileEnding=Bisq Onion Address Backup (*.bisq) +settings.net.importAddressButton=Restore address +settings.net.importAddressFileDialog=Select the backup file +settings.net.importAddress=Please be aware that you will loose your reputation when you restore a previous onion address (but restore your reputation connected to the restored onion address). Furthermore, you need to restart the application to set your new onion address active.\n\nProceed? +settings.net.importAddressError=Bisq could not import the backup file you specified. Be sure you chose a valid file and the file exists. If you are sure everything has been correct, please proceed below. settings.net.btcNodesLabel=Use custom Bitcoin Core nodes settings.net.bitcoinPeersLabel=Connected peers settings.net.useTorForBtcJLabel=Use Tor for Bitcoin network diff --git a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java index 7271458698f..b1608915722 100644 --- a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java +++ b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java @@ -73,6 +73,7 @@ import javafx.collections.transformation.SortedList; import java.io.File; +import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -183,7 +184,7 @@ public void initialize() { reSyncSPVChainButton.updateText(Res.get("settings.net.reSyncSPVChainButton")); renewIdButton.updateText(Res.get("settings.net.renewAddressButton")); exportIdButton.updateText(Res.get("settings.net.exportAddressButton")); - importIdButton.updateText("Import ID"); + importIdButton.updateText(Res.get("settings.net.importAddressButton")); p2PPeersLabel.setText(Res.get("settings.net.p2PPeersLabel")); onionAddressColumn.setGraphic(new AutoTooltipLabel(Res.get("settings.net.onionAddressColumn"))); onionAddressColumn.getStyleClass().add("first-column"); @@ -315,6 +316,27 @@ public void activate() { p2PService.exportHiddenService(file); }); + importIdButton.setOnAction(event -> { + new Popup<>().information(Res.get("settings.net.importAddress")) + .actionButtonText(Res.get("shared.applyAndShutDown")) + .onAction(() -> { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle(Res.get("settings.net.importAddressFileDialog")); + fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(Res.get("settings.net.exportAddressFileEnding"), "*.bisq")); + File file = fileChooser.showOpenDialog(root.getScene().getWindow()); + if (file == null) + return; + try { + p2PService.importHiddenService(file); + UserThread.runAfter(BisqApp.getShutDownHandler()::run, 500, TimeUnit.MILLISECONDS); + } catch (IOException e) { + new Popup<>().error(Res.get("settings.net.importAddressError")).show(); + } + }) + .closeButtonText(Res.get("shared.cancel")) + .show(); + }); + bitcoinPeersSubscription = EasyBind.subscribe(walletsSetup.connectedPeersProperty(), connectedPeers -> updateBitcoinPeersTable()); @@ -359,6 +381,7 @@ public void deactivate() { renewIdButton.setOnAction(null); exportIdButton.setOnAction(null); + importIdButton.setOnAction(null); if (nodeAddressSubscription != null) nodeAddressSubscription.unsubscribe(); diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index 6bf93c9d76d..558d7f47a4d 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -75,6 +75,7 @@ import java.security.PublicKey; import java.io.File; +import java.io.IOException; import java.util.Arrays; import java.util.HashMap; @@ -944,4 +945,8 @@ public void reportRequiredHiddenServices(Set nodeAddressList) { public void exportHiddenService(File file) { networkNode.exportHiddenService(file); } + + public void importHiddenService(File file) throws IOException { + networkNode.importHiddenService(file); + } } diff --git a/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java index 8d809abc633..fe9757e7fe8 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/LocalhostNetworkNode.java @@ -87,8 +87,8 @@ public void start(@Nullable SetupListener setupListener) { } @Override - public void renewHiddenService() { - + public File renewHiddenService() { + return null; } // Called from NetworkNode thread @@ -106,4 +106,9 @@ public void clearHiddenServices(Set nodeAddressList) { public void exportHiddenService(File file) { } + + @Override + public void importHiddenService(File source) { + + } } diff --git a/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java index afb75746c65..13d8cf3b39a 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java @@ -232,7 +232,7 @@ public void onFailure(@NotNull Throwable throwable) { } } - public abstract void renewHiddenService(); + public abstract File renewHiddenService(); /** * Gets rid of all hidden services except those given in the retain @@ -244,6 +244,8 @@ public void onFailure(@NotNull Throwable throwable) { public abstract void exportHiddenService(File target); + public abstract void importHiddenService(File source) throws IOException; + @Nullable private InboundConnection getInboundConnection(@NotNull NodeAddress peersNodeAddress) { Optional inboundConnectionOptional = lookupInBoundConnection(peersNodeAddress); diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java index 2ea46ff00e6..eedde1fa094 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java @@ -68,6 +68,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import org.slf4j.Logger; @@ -121,9 +122,10 @@ public TorNetworkNode(int servicePort, NetworkProtoResolver networkProtoResolver /** * only prepares a fresh hidden service folder. The actual HS is only established and * started on Bisq restart! + * @return */ @Override - public void renewHiddenService() { + public File renewHiddenService() { // find suitable folder name int seed = 0; File newDir = null; @@ -134,6 +136,8 @@ public void renewHiddenService() { } while (newDir.exists()); newDir.mkdirs(); + + return newDir; } /** @@ -201,6 +205,38 @@ public void exportHiddenService(File target) { } } + @Override + public void importHiddenService(File source) throws IOException { + if (!source.getName().endsWith(".bisq")) { + log.error("Tried to import from a file not ending in '.bisq'"); + throw new IOException("Cannot read backup file."); + } + + try { + // create hidden service directory + File newHiddenServiceDir = renewHiddenService(); + + // unzip contents of source + byte[] buffer = new byte[1024]; + ZipInputStream zis = new ZipInputStream(new FileInputStream(source)); + ZipEntry zipEntry = zis.getNextEntry(); + while (zipEntry != null) { + File destination = new File(newHiddenServiceDir, zipEntry.getName()); + FileOutputStream fos = new FileOutputStream(destination); + int len; + while ((len = zis.read(buffer)) > 0) + fos.write(buffer, 0, len); + fos.close(); + zipEntry = zis.getNextEntry(); + } + zis.closeEntry(); + zis.close(); + } catch (IOException e) { + log.error("Importing a hidden service failed. ", e); + throw e; + } + } + @Override public void start(@Nullable SetupListener setupListener) { if (setupListener != null) From ea4d4e1693ffdfc78ebe03c50a65db719e39954a Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Thu, 1 Aug 2019 11:01:21 +0200 Subject: [PATCH 13/24] DE translations --- .../src/main/resources/i18n/displayStrings_de.properties | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index 4d57eabc8fc..161b2e5f53d 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -940,6 +940,15 @@ setting.preferences.dao.fullNodeInfo.cancel=Nein, ich möchte weiterhin den Lite settings.net.btcHeader=Bitcoin-Netzwerk settings.net.p2pHeader=Bisq-Netzwerk settings.net.onionAddressLabel=Meine Onion-Adresse +settings.net.renewAddressButton=Neue Adresse verwenden +settings.net.renewAddress=Ihre Reputation ist an die aktuelle Onion-Adresse gebunden. Wenn Sie die aktuelle Adresse ersetzen, verlieren Sie auch die damit verbundene Reputation. Die neue Adresse wird nach einem Neustart der Software aktiv.\n\nWollen Sie den Adresswechsel anwenden und die Software neu starten? +settings.net.exportAddressButton=Adresse sichern +settings.net.exportAddressFileDialog=Sicherungsdatei auswählen +settings.net.exportAddressFileEnding=Bisq Onion Adresse Sicherungsdatei (*.bisq) +settings.net.importAddressButton=Adresse wiederherstellen +settings.net.importAddressFileDialog=Sicherungsdatei auswählen +settings.net.importAddress=Ihre Reputation ist an die aktuelle Onion-Adresse gebunden. Wenn Sie die aktuelle Adresse ersetzen, verlieren Sie auch die damit verbundene Reputation. Dafür wird mit der Wiederherstellung eine alten Adresse deren Reputation wieder aktiv. Die neue Adresse wird nach einem Neustart der Software aktiv.\n\nWollen Sie eine Adresswiederherstellung anwenden und die Software neu starten? +settings.net.importAddressError=Wiederherstellen der Sicherung war nicht erfolgreich. Stellen Sie sicher, dass Sie die korrekte Sicherungsdatei ausgewählt haben und diese auch existiert und versuchen Sie es erneut. Sollte das Problem wiederholt auftreten, bitte beachten Sie die nachfolgenden Hinweise. settings.net.btcNodesLabel=Spezifische Bitcoin-Core-Knoten verwenden settings.net.bitcoinPeersLabel=Verbundene Peers settings.net.useTorForBtcJLabel=Tor für das Bitcoin-Netzwerk verwenden From bc9f95fbad65cfaf3408bd9a8f5169ee1a6a37a8 Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Thu, 1 Aug 2019 11:44:42 +0200 Subject: [PATCH 14/24] Handle duplicate hidden service situation --- .../network/p2p/network/TorNetworkNode.java | 51 +++++++++++-------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java index eedde1fa094..b50255c0e83 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java @@ -154,17 +154,21 @@ public void clearHiddenServices(Set retain) { // so they are gone after an app restart nodeAddressToHSDirectory.entrySet().stream().filter(nodeAddressFileEntry -> !retain.contains(nodeAddressFileEntry.getKey())) .forEach(nodeAddressFileEntry -> { - try { - Files.walk(nodeAddressFileEntry.getValue().toPath()) - .sorted() - .map(Path::toFile) - .forEach(File::deleteOnExit); - } catch (IOException e) { - log.error("Error while trying to delete deprecated hidden service directory", e); - } + deleteHiddenServiceDir(nodeAddressFileEntry.getValue()); }); } + private void deleteHiddenServiceDir(File dir) { + try { + Files.walk(dir.toPath()) + .sorted() + .map(Path::toFile) + .forEach(File::deleteOnExit); + } catch (IOException e) { + log.error("Error while trying to delete deprecated hidden service directory", e); + } + } + @Override public void exportHiddenService(File target) { // is directory? @@ -261,24 +265,31 @@ public void start(@Nullable SetupListener setupListener) { // find hidden service candidates File[] hiddenServiceDirs = torMode.getHiddenServiceBaseDirectory().listFiles(); - // sort in ascending order - Arrays.sort(hiddenServiceDirs, Comparator.comparing(File::getName)); - // start CountDownLatch gate = new CountDownLatch(hiddenServiceDirs.length); nodeAddressToHSDirectory.clear(); - NodeAddress nodeAddress = null; - for (File current : hiddenServiceDirs) - if (current.isDirectory()) { - nodeAddress = createHiddenService(current.getName(), Utils.findFreeSystemPort(), servicePort, gate); - FileUtil.rollingBackup(current, "private_key", 20); + // sort newest first, so we can just mark duplicate services for deletion + Arrays.stream(hiddenServiceDirs).sorted(Comparator.comparing(File::getName).reversed()) + .forEachOrdered(current -> { + try { + NodeAddress nodeAddress = createHiddenService(current.getName(), Utils.findFreeSystemPort(), servicePort, gate); - nodeAddressToHSDirectory.put(nodeAddress, current); - } + // use newest HS as for NodeAddress + if (nodeAddressProperty.get() == null) + nodeAddressProperty.set(nodeAddress); + + FileUtil.rollingBackup(current, "private_key", 20); + + nodeAddressToHSDirectory.put(nodeAddress, current); + } catch (Exception e) { + if (e instanceof IOException && e.getMessage().contains("collision")) + deleteHiddenServiceDir(current); + else + throw e; + } + }); - // use newest HS as for NodeAddress - nodeAddressProperty.set(nodeAddress); UserThread.execute(() -> setupListeners.forEach(SetupListener::onTorNodeReady)); // only report HiddenServicePublished once all are published From 87f006acf00fbb9fc99b54cacb50a158f1b0dd65 Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Wed, 14 Aug 2019 20:09:25 +0200 Subject: [PATCH 15/24] Report if HS(s) fail to start in time --- .../main/java/bisq/network/p2p/network/TorNetworkNode.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java index b50255c0e83..1ece3b80da3 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java @@ -293,7 +293,11 @@ public void start(@Nullable SetupListener setupListener) { UserThread.execute(() -> setupListeners.forEach(SetupListener::onTorNodeReady)); // only report HiddenServicePublished once all are published - gate.await(90, TimeUnit.SECONDS); + if (!gate.await(90, TimeUnit.SECONDS)) { + log.error("{} hidden services failed to start in time.", gate.getCount()); + String msg = "Some hidden services failed to start in time."; + UserThread.execute(() -> setupListeners.forEach(s -> s.onSetupFailed(new RuntimeException(msg)))); + } UserThread.execute(() -> setupListeners.forEach(SetupListener::onHiddenServicePublished)); return null; From 612e8f7df0e1bdee658be35c0d84cb32142f51cb Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Wed, 14 Aug 2019 20:10:10 +0200 Subject: [PATCH 16/24] Adjust expected HS ready events --- .../main/java/bisq/network/p2p/network/TorNetworkNode.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java index 1ece3b80da3..e5526ec13df 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java @@ -283,9 +283,10 @@ public void start(@Nullable SetupListener setupListener) { nodeAddressToHSDirectory.put(nodeAddress, current); } catch (Exception e) { - if (e instanceof IOException && e.getMessage().contains("collision")) + if (e instanceof IOException && e.getMessage().contains("collision")) { deleteHiddenServiceDir(current); - else + gate.countDown(); + } else throw e; } }); From 368a6eeff4bacb442ff25d41d92bee9d5891ac08 Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Wed, 14 Aug 2019 21:06:21 +0200 Subject: [PATCH 17/24] Ignore HS dirs like .DS_Store --- p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java index e5526ec13df..160eae0a5d4 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java @@ -263,7 +263,7 @@ public void start(@Nullable SetupListener setupListener) { } // find hidden service candidates - File[] hiddenServiceDirs = torMode.getHiddenServiceBaseDirectory().listFiles(); + File[] hiddenServiceDirs = torMode.getHiddenServiceBaseDirectory().listFiles((dir, name) -> name.matches("\\d+")); // start CountDownLatch gate = new CountDownLatch(hiddenServiceDirs.length); From 0dca7d3a4d6fcbe443168dbf39a76dd0b03b768a Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Tue, 20 Aug 2019 13:36:15 +0200 Subject: [PATCH 18/24] Streamline button text (+ DE translation) --- core/src/main/resources/i18n/displayStrings.properties | 2 +- core/src/main/resources/i18n/displayStrings_de.properties | 2 +- .../bisq/desktop/main/settings/network/NetworkSettingsView.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index fbcc4783cdf..670bb28357d 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -1080,7 +1080,7 @@ settings.net.exportAddressFileDialog=Select the backup file settings.net.exportAddressFileEnding=Bisq Onion Address Backup (*.bisq) settings.net.importAddressButton=Restore address settings.net.importAddressFileDialog=Select the backup file -settings.net.importAddress=Please be aware that you will loose your reputation when you restore a previous onion address (but restore your reputation connected to the restored onion address). Furthermore, you need to restart the application to set your new onion address active.\n\nProceed? +settings.net.importAddress=Please be aware that you will loose your reputation when you restore a previous onion address (but restore your reputation connected to the restored onion address). Furthermore, you need to restart the application to set your new onion address active. settings.net.importAddressError=Bisq could not import the backup file you specified. Be sure you chose a valid file and the file exists. If you are sure everything has been correct, please proceed below. settings.net.btcNodesLabel=Use custom Bitcoin Core nodes settings.net.bitcoinPeersLabel=Connected peers diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index 161b2e5f53d..dd4ccf231ba 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -947,7 +947,7 @@ settings.net.exportAddressFileDialog=Sicherungsdatei auswählen settings.net.exportAddressFileEnding=Bisq Onion Adresse Sicherungsdatei (*.bisq) settings.net.importAddressButton=Adresse wiederherstellen settings.net.importAddressFileDialog=Sicherungsdatei auswählen -settings.net.importAddress=Ihre Reputation ist an die aktuelle Onion-Adresse gebunden. Wenn Sie die aktuelle Adresse ersetzen, verlieren Sie auch die damit verbundene Reputation. Dafür wird mit der Wiederherstellung eine alten Adresse deren Reputation wieder aktiv. Die neue Adresse wird nach einem Neustart der Software aktiv.\n\nWollen Sie eine Adresswiederherstellung anwenden und die Software neu starten? +settings.net.importAddress=Ihre Reputation ist an die aktuelle Onion-Adresse gebunden. Wenn Sie die aktuelle Adresse ersetzen, verlieren Sie auch die damit verbundene Reputation. Dafür wird mit der Wiederherstellung einer alten Adresse deren Reputation wieder aktiv. Die neue Adresse wird nach einem Neustart der Software aktiv. settings.net.importAddressError=Wiederherstellen der Sicherung war nicht erfolgreich. Stellen Sie sicher, dass Sie die korrekte Sicherungsdatei ausgewählt haben und diese auch existiert und versuchen Sie es erneut. Sollte das Problem wiederholt auftreten, bitte beachten Sie die nachfolgenden Hinweise. settings.net.btcNodesLabel=Spezifische Bitcoin-Core-Knoten verwenden settings.net.bitcoinPeersLabel=Verbundene Peers diff --git a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java index b1608915722..bdc3e8de331 100644 --- a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java +++ b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java @@ -318,7 +318,7 @@ public void activate() { importIdButton.setOnAction(event -> { new Popup<>().information(Res.get("settings.net.importAddress")) - .actionButtonText(Res.get("shared.applyAndShutDown")) + .actionButtonText(Res.get("settings.net.importAddressFileDialog")) .onAction(() -> { FileChooser fileChooser = new FileChooser(); fileChooser.setTitle(Res.get("settings.net.importAddressFileDialog")); From 7c883b195083a3978d89cdf010da0c21faec8eb1 Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Tue, 20 Aug 2019 13:42:19 +0200 Subject: [PATCH 19/24] Cleanup --- .../java/bisq/network/p2p/network/NewTor.java | 6 --- .../bisq/network/p2p/network/RunningTor.java | 6 --- .../bisq/network/p2p/network/TorMode.java | 38 ++----------------- 3 files changed, 4 insertions(+), 46 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/network/NewTor.java b/p2p/src/main/java/bisq/network/p2p/network/NewTor.java index 9e84d72e659..cf41851e390 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/NewTor.java +++ b/p2p/src/main/java/bisq/network/p2p/network/NewTor.java @@ -110,10 +110,4 @@ public Tor getTor() throws IOException, TorCtlException { return result; } - - @Override - public File getHiddenServiceBaseDirectory() { - return new File(torDir, "hiddenservice"); - } - } diff --git a/p2p/src/main/java/bisq/network/p2p/network/RunningTor.java b/p2p/src/main/java/bisq/network/p2p/network/RunningTor.java index 0de3956345f..d86fa7892b3 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/RunningTor.java +++ b/p2p/src/main/java/bisq/network/p2p/network/RunningTor.java @@ -77,10 +77,4 @@ else if (cookieFile.exists()) return result; } - - @Override - public File getHiddenServiceBaseDirectory() { - return new File(torDir, HIDDEN_SERVICE_DIRECTORY); - } - } diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorMode.java b/p2p/src/main/java/bisq/network/p2p/network/TorMode.java index 26d2ee0c8b0..84a6b3c6cc7 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorMode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorMode.java @@ -28,28 +28,17 @@ /** * Holds information on how tor should be created and delivers a respective * {@link Tor} object when asked. - * + * * @author Florian Reimair * */ public abstract class TorMode { - /** - * The sub-directory where the private_key file sits in. Kept - * private, because it only concerns implementations of {@link TorMode}. - */ - protected static final String HIDDEN_SERVICE_DIRECTORY = "hiddenservice"; - protected final File torDir; /** * @param torDir points to the place, where we will persist private * key and address data - * @param hiddenServiceDir The directory where the private_key file - * sits in. Note that, due to the inner workings of the - * Netlayer dependency, it does not - * necessarily equal - * {@link TorMode#getHiddenServiceBaseDirectory()}. */ public TorMode(File torDir) { this.torDir = torDir; @@ -57,33 +46,14 @@ public TorMode(File torDir) { /** * Returns a fresh {@link Tor} object. - * + * * @return a fresh instance of {@link Tor} * @throws IOException * @throws TorCtlException */ public abstract Tor getTor() throws IOException, TorCtlException; - /** - * {@link NativeTor}'s inner workings prepend its Tor installation path and some - * other stuff to the hiddenServiceDir, thus, selecting nothing (i.e. - * "") as a hidden service directory is fine. {@link ExternalTor}, - * however, does not have a Tor installation path and thus, takes the hidden - * service path literally. Hence, we set "torDir/hiddenservice" as - * the hidden service directory. By doing so, we use the same - * private_key file as in {@link NewTor} mode. - * - * @return "" in {@link NewTor} Mode, - * "torDir/externalTorHiddenService" in {@link RunningTor} - * mode - */ - public abstract File getHiddenServiceBaseDirectory(); - - /** - * Do a rolling backup of the "private_key" file. - */ - protected void doRollingBackup() { - FileUtil.rollingBackup(new File(torDir, HIDDEN_SERVICE_DIRECTORY), "private_key", 20); + public File getHiddenServiceBaseDirectory() { + return new File(torDir, "hiddenservice"); } - } From 91255ef9ff343851ccbd2024e95a78b4e70eb0e0 Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Tue, 20 Aug 2019 13:46:42 +0200 Subject: [PATCH 20/24] Refactored migration and validation --- .../main/java/bisq/network/p2p/network/TorNetworkNode.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java index 160eae0a5d4..d1d94f05c5f 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java @@ -256,14 +256,13 @@ public void start(@Nullable SetupListener setupListener) { // see if we have to migrate the old file structure if (torMode.getHiddenServiceBaseDirectory().listFiles((dir, name) -> name.equals("hostname")).length > 0) { - File newHiddenServiceDirectory = new File(torMode.getHiddenServiceBaseDirectory(), "0"); - newHiddenServiceDirectory.mkdir(); + File newHiddenServiceDirectory = renewHiddenService(); for (File current : torMode.getHiddenServiceBaseDirectory().listFiles()) current.renameTo(new File(newHiddenServiceDirectory, current.getName())); } // find hidden service candidates - File[] hiddenServiceDirs = torMode.getHiddenServiceBaseDirectory().listFiles((dir, name) -> name.matches("\\d+")); + File[] hiddenServiceDirs = torMode.getHiddenServiceBaseDirectory().listFiles((dir, name) -> name.matches("\\d{15,}")); // start CountDownLatch gate = new CountDownLatch(hiddenServiceDirs.length); From c3184e04df1125b7f526ace00e656c6b42c345fd Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Thu, 5 Sep 2019 13:51:16 +0200 Subject: [PATCH 21/24] Added tests but ignore them for now Tests run smoothly on my machine, Travis shows successes but also fails on the same code base. I disable the tests for now, maybe someone can try to reproduce the failing tests? --- .../network/p2p/network/TorNetworkNode.java | 4 +- .../bisq/network/p2p/network/MultiHSTest.java | 238 ++++++++++++++++++ p2p/src/test/resources/logback.xml | 14 ++ 3 files changed, 254 insertions(+), 2 deletions(-) create mode 100644 p2p/src/test/java/bisq/network/p2p/network/MultiHSTest.java create mode 100644 p2p/src/test/resources/logback.xml diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java index d1d94f05c5f..7a0930de2b7 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java @@ -161,9 +161,9 @@ public void clearHiddenServices(Set retain) { private void deleteHiddenServiceDir(File dir) { try { Files.walk(dir.toPath()) - .sorted() + .sorted(Comparator.reverseOrder()) .map(Path::toFile) - .forEach(File::deleteOnExit); + .forEach(File::delete); } catch (IOException e) { log.error("Error while trying to delete deprecated hidden service directory", e); } diff --git a/p2p/src/test/java/bisq/network/p2p/network/MultiHSTest.java b/p2p/src/test/java/bisq/network/p2p/network/MultiHSTest.java new file mode 100644 index 00000000000..2954d69c9a1 --- /dev/null +++ b/p2p/src/test/java/bisq/network/p2p/network/MultiHSTest.java @@ -0,0 +1,238 @@ +/* + * This file is part of Bisq. + * + * Bisq 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. + * + * Bisq 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 Bisq. If not, see . + */ + +package bisq.network.p2p.network; + +import bisq.network.p2p.TestUtils; + +import java.io.File; +import java.io.IOException; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +/** + * Tests functionality around the export and import hidden service address feature. Please + * be aware that these tests are not exhaustive. + */ +@SuppressWarnings("ConstantConditions") +@Ignore +public class MultiHSTest { + int port = 9001; + static File torWorkingDir = new File(MultiHSTest.class.getSimpleName()); + static File hiddenServiceDir = new File(torWorkingDir, "hiddenservice"); + static String hiddenServiceDirPattern = "\\d{15}"; + static File exportFile = new File(torWorkingDir, "export.bisq"); + + /** + * Device(s) Under Test + */ + TorNetworkNode DUT, DUT2; + + @Before + public void setup() { + DUT = new TorNetworkNode(port, TestUtils.getNetworkProtoResolver(), false, + new NewTor(torWorkingDir, "", "", new ArrayList<>())); + DUT2 = new TorNetworkNode(port, TestUtils.getNetworkProtoResolver(), false, + new NewTor(torWorkingDir, "", "", new ArrayList<>())); + } + + @After + public void cleanup() { + cleanupRecursively(hiddenServiceDir); + if (exportFile.exists()) + exportFile.delete(); + } + + @AfterClass + public static void cleanupThoroughly() { + cleanupRecursively(torWorkingDir); + } + + static void cleanupRecursively(File current) { + if (current.isDirectory()) + for (File child : current.listFiles()) + cleanupRecursively(child); + + current.delete(); + } + + static void checkHiddenServiceDirs(boolean checkContent) { + for (String current : hiddenServiceDir.list()) + Assert.assertTrue(current.matches(hiddenServiceDirPattern)); + + if (!checkContent) + return; + + for (File current : hiddenServiceDir.listFiles()) + // if there are 3 file, a "backup" dir likely is one of them + Assert.assertEquals(3, current.list().length); + } + + static void startAndStopDUT(TorNetworkNode DUT) throws InterruptedException { + CountDownLatch latch = new CountDownLatch(1); + + DUT.start(new SetupListener() { + @Override + public void onTorNodeReady() { + try { + Thread.sleep(2000); // sleep to give other listeners a change to do their stuff. If for example the listener responsible for starting the HS is executed after we already shut down tor, there would be no hs files to export + } catch (InterruptedException e) { + e.printStackTrace(); + } + DUT.shutDown(() -> { + }); + latch.countDown(); + } + + @Override + public void onHiddenServicePublished() { + latch.countDown(); + } + + @Override + public void onSetupFailed(Throwable throwable) { + Assert.fail("setup failed"); + } + + @Override + public void onRequestCustomBridges() { + Assert.fail("requested custom bridges"); + } + }); + latch.await(10, TimeUnit.SECONDS); + } + + // - start the app and see if one hidden service is created + @Test + public void firstLaunch() throws InterruptedException { + startAndStopDUT(DUT); + + Assert.assertEquals(1, hiddenServiceDir.list().length); + checkHiddenServiceDirs(true); + } + + // - renew the hidden service and see if 2 are active + @Test + public void renewHiddenService() throws InterruptedException { + DUT.renewHiddenService(); + DUT.renewHiddenService(); + Assert.assertEquals(2, hiddenServiceDir.list().length); + checkHiddenServiceDirs(false); + } + + // - migrate to new structure + @Test + public void migrateHiddenService() throws InterruptedException, IOException { + // get ourselves a valid data structure + startAndStopDUT(DUT); + + // move files to old structure + File source = hiddenServiceDir.listFiles()[0]; + for (File current : source.listFiles()) + current.renameTo(new File(hiddenServiceDir, current.getName())); + source.delete(); + + // start up + startAndStopDUT(DUT2); + + // and see if things got migrated correctly + Assert.assertEquals(1, hiddenServiceDir.list().length); + checkHiddenServiceDirs(true); + } + + // - export hidden service + @Test + public void exportHiddenService() throws InterruptedException, IOException { + startAndStopDUT(DUT); + + DUT.exportHiddenService(exportFile); + + Assert.assertTrue(exportFile.exists()); + Assert.assertTrue(0 < exportFile.length()); + } + + // - import hidden service + @Test + public void importHiddenService() throws InterruptedException, IOException { + startAndStopDUT(DUT); + + DUT.exportHiddenService(exportFile); + + DUT.renewHiddenService(); + + startAndStopDUT(DUT2); + + DUT2.clearHiddenServices(new HashSet<>()); + + DUT2.importHiddenService(exportFile); + + Assert.assertEquals(2, hiddenServiceDir.list().length); + } + + // - duplicate hidden service and see if only one is started up and the other one is deleted + @Test + public void importDuplicateHiddenService() throws InterruptedException, IOException { + startAndStopDUT(DUT); + + DUT.exportHiddenService(exportFile); + DUT.importHiddenService(exportFile); + + startAndStopDUT(DUT2); + + Assert.assertEquals(1, hiddenServiceDir.list().length); + } + + // - remove hidden services and see if they are removed + @Test + public void removeHiddenService() throws InterruptedException { + DUT.renewHiddenService(); + DUT.renewHiddenService(); + Assert.assertEquals(2, hiddenServiceDir.list().length); + + startAndStopDUT(DUT); + + DUT.clearHiddenServices(new HashSet<>()); + + Assert.assertNotEquals("Even the current HS has been deleted!", 0, hiddenServiceDir.list().length); + Assert.assertEquals(1, hiddenServiceDir.list().length); + + checkHiddenServiceDirs(true); + } + + // - simulate MacOSs .DS_STORE file + @Test + public void macsDsStore() throws InterruptedException, IOException { + DUT.renewHiddenService(); + + File dsStoreFile = new File(hiddenServiceDir, ".DS_STORE"); + dsStoreFile.mkdir(); + + startAndStopDUT(DUT); + + Assert.assertEquals(0, dsStoreFile.list().length); + } +} diff --git a/p2p/src/test/resources/logback.xml b/p2p/src/test/resources/logback.xml new file mode 100644 index 00000000000..8cea861c79d --- /dev/null +++ b/p2p/src/test/resources/logback.xml @@ -0,0 +1,14 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + From 4361cb81c7657d2ca9e63045d5478884cdea201e Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Tue, 19 Nov 2019 17:10:42 +0100 Subject: [PATCH 22/24] Prevent connecting to self In the peer exchange procedure, a remote client will report one of our own passive network nodes (nodes that have been used in the past, have been replaced by a new active node, but are kept until all ongoing trades are completed) --- .../main/java/bisq/network/p2p/network/NetworkNode.java | 9 +++++++++ .../java/bisq/network/p2p/network/TorNetworkNode.java | 5 +++++ .../main/java/bisq/network/p2p/peers/PeerManager.java | 3 ++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java index 13d8cf3b39a..ab6b7705ce4 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java @@ -483,4 +483,13 @@ private void printInboundConnections() { public NodeAddress getNodeAddress() { return nodeAddressProperty.get(); } + + /** + * Returns a collection of node addresses, which have been used in the past and are + * kept active until no longer needed. + * @return + */ + public Set getPassiveNodeAddresses() { + return new HashSet<>(); + } } diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java index 7a0930de2b7..d670538b926 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java @@ -241,6 +241,11 @@ public void importHiddenService(File source) throws IOException { } } + @Override + public Set getPassiveNodeAddresses() { + return nodeAddressToHSDirectory.keySet(); + } + @Override public void start(@Nullable SetupListener setupListener) { if (setupListener != null) diff --git a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java index 0b918328ba9..4bcdc47cfe9 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -584,7 +584,8 @@ public boolean isSelf(Peer reportedPeer) { } public boolean isSelf(NodeAddress nodeAddress) { - return nodeAddress.equals(networkNode.getNodeAddress()); + + return nodeAddress.equals(networkNode.getNodeAddress()) || networkNode.getPassiveNodeAddresses().contains(nodeAddress); } public boolean isConfirmed(Peer reportedPeer) { From ae20a60465c6ce59165f924de3edb22e1864d9fe Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Wed, 20 Nov 2019 10:31:54 +0100 Subject: [PATCH 23/24] Incoming messages get accepted if addressed to us Previously, only one node address was active and the checks have only been done for this address. Now that there are multiple active node addresses, we have to perform the check to respect ALL our nodes --- p2p/src/main/java/bisq/network/p2p/P2PService.java | 10 +++++----- .../java/bisq/network/p2p/network/NetworkNode.java | 6 +++--- .../java/bisq/network/p2p/network/TorNetworkNode.java | 2 +- .../main/java/bisq/network/p2p/peers/PeerManager.java | 3 +-- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/p2p/src/main/java/bisq/network/p2p/P2PService.java b/p2p/src/main/java/bisq/network/p2p/P2PService.java index 558d7f47a4d..e6c07be31db 100644 --- a/p2p/src/main/java/bisq/network/p2p/P2PService.java +++ b/p2p/src/main/java/bisq/network/p2p/P2PService.java @@ -910,14 +910,14 @@ public KeyRing getKeyRing() { /////////////////////////////////////////////////////////////////////////////////////////// private boolean verifyAddressPrefixHash(PrefixedSealedAndSignedMessage prefixedSealedAndSignedMessage) { - if (networkNode.getNodeAddress() != null) { - byte[] blurredAddressHash = networkNode.getNodeAddress().getAddressPrefixHash(); - return blurredAddressHash != null && - Arrays.equals(blurredAddressHash, prefixedSealedAndSignedMessage.getAddressPrefixHash()); - } else { + if (networkNode.getNodeAddress() == null) { log.debug("myOnionAddress is null at verifyAddressPrefixHash. That is expected at startup."); return false; } + + Set activeNodeAddresses = networkNode.getActiveNodeAddresses(); + + return activeNodeAddresses.stream().map(nodeAddress -> Arrays.equals(nodeAddress.getAddressPrefixHash(), prefixedSealedAndSignedMessage.getAddressPrefixHash())).reduce(false, (a, b) -> a || b); } /** diff --git a/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java index ab6b7705ce4..2e3c86766a3 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java @@ -485,11 +485,11 @@ public NodeAddress getNodeAddress() { } /** - * Returns a collection of node addresses, which have been used in the past and are + * Returns a collection of node addresses which have been used in the past and are * kept active until no longer needed. * @return */ - public Set getPassiveNodeAddresses() { - return new HashSet<>(); + public Set getActiveNodeAddresses() { + return Set.of(getNodeAddress()); } } diff --git a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java index d670538b926..b10efac229b 100644 --- a/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java +++ b/p2p/src/main/java/bisq/network/p2p/network/TorNetworkNode.java @@ -242,7 +242,7 @@ public void importHiddenService(File source) throws IOException { } @Override - public Set getPassiveNodeAddresses() { + public Set getActiveNodeAddresses() { return nodeAddressToHSDirectory.keySet(); } diff --git a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java index 4bcdc47cfe9..6b1a1a4dfe3 100644 --- a/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java +++ b/p2p/src/main/java/bisq/network/p2p/peers/PeerManager.java @@ -584,8 +584,7 @@ public boolean isSelf(Peer reportedPeer) { } public boolean isSelf(NodeAddress nodeAddress) { - - return nodeAddress.equals(networkNode.getNodeAddress()) || networkNode.getPassiveNodeAddresses().contains(nodeAddress); + return networkNode.getActiveNodeAddresses().contains(nodeAddress); } public boolean isConfirmed(Peer reportedPeer) { From 029dc3da78b5c1d411f6186686fef942528a5ae7 Mon Sep 17 00:00:00 2001 From: Florian Reimair Date: Thu, 21 Nov 2019 18:30:50 +0100 Subject: [PATCH 24/24] Fix compilation error --- .../desktop/main/settings/network/NetworkSettingsView.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java index bdc3e8de331..f02bbe7cc4e 100644 --- a/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java +++ b/desktop/src/main/java/bisq/desktop/main/settings/network/NetworkSettingsView.java @@ -297,7 +297,7 @@ public void activate() { reSyncSPVChainButton.setOnAction(event -> GUIUtil.reSyncSPVChain(preferences)); renewIdButton.setOnAction(event -> { - new Popup<>().information(Res.get("settings.net.renewAddress")) + new Popup().information(Res.get("settings.net.renewAddress")) .actionButtonText(Res.get("shared.applyAndShutDown")) .onAction(() -> { p2PService.renewHiddenService(); @@ -317,7 +317,7 @@ public void activate() { }); importIdButton.setOnAction(event -> { - new Popup<>().information(Res.get("settings.net.importAddress")) + new Popup().information(Res.get("settings.net.importAddress")) .actionButtonText(Res.get("settings.net.importAddressFileDialog")) .onAction(() -> { FileChooser fileChooser = new FileChooser(); @@ -330,7 +330,7 @@ public void activate() { p2PService.importHiddenService(file); UserThread.runAfter(BisqApp.getShutDownHandler()::run, 500, TimeUnit.MILLISECONDS); } catch (IOException e) { - new Popup<>().error(Res.get("settings.net.importAddressError")).show(); + new Popup().error(Res.get("settings.net.importAddressError")).show(); } }) .closeButtonText(Res.get("shared.cancel"))