mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-05-17 14:00:35 -04:00
Fix authentication issues, store address
This commit is contained in:
parent
f8adaaed62
commit
85427d7bde
22 changed files with 553 additions and 598 deletions
|
@ -78,6 +78,15 @@ public class Storage<T extends Serializable> {
|
||||||
this.dir = dir;
|
this.dir = dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public T initAndGetPersisted(String fileName) {
|
||||||
|
this.fileName = fileName;
|
||||||
|
storageFile = new File(dir, fileName);
|
||||||
|
fileManager = new FileManager<>(dir, storageFile, 600, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
|
return getPersisted();
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public T initAndGetPersisted(T serializable) {
|
public T initAndGetPersisted(T serializable) {
|
||||||
return initAndGetPersisted(serializable, serializable.getClass().getSimpleName());
|
return initAndGetPersisted(serializable, serializable.getClass().getSimpleName());
|
||||||
|
@ -90,15 +99,23 @@ public class Storage<T extends Serializable> {
|
||||||
storageFile = new File(dir, fileName);
|
storageFile = new File(dir, fileName);
|
||||||
fileManager = new FileManager<>(dir, storageFile, 600, TimeUnit.MILLISECONDS);
|
fileManager = new FileManager<>(dir, storageFile, 600, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
return getPersisted(serializable);
|
return getPersisted();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void queueUpForSave() {
|
||||||
|
queueUpForSave(serializable);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save delayed and on a background thread
|
// Save delayed and on a background thread
|
||||||
public void queueUpForSave() {
|
public void queueUpForSave(T serializable) {
|
||||||
|
if (serializable != null) {
|
||||||
log.trace("save " + fileName);
|
log.trace("save " + fileName);
|
||||||
checkNotNull(storageFile, "storageFile = null. Call setupFileStorage before using read/write.");
|
checkNotNull(storageFile, "storageFile = null. Call setupFileStorage before using read/write.");
|
||||||
|
|
||||||
fileManager.saveLater(serializable);
|
fileManager.saveLater(serializable);
|
||||||
|
} else {
|
||||||
|
log.trace("queueUpForSave called but no serializable set");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove(String fileName) {
|
public void remove(String fileName) {
|
||||||
|
@ -113,17 +130,17 @@ public class Storage<T extends Serializable> {
|
||||||
// We do the file read on the UI thread to avoid problems from multi threading.
|
// We do the file read on the UI thread to avoid problems from multi threading.
|
||||||
// Data are small and read is done only at startup, so it is no performance issue.
|
// Data are small and read is done only at startup, so it is no performance issue.
|
||||||
@Nullable
|
@Nullable
|
||||||
private T getPersisted(T serializable) {
|
private T getPersisted() {
|
||||||
if (storageFile.exists()) {
|
if (storageFile.exists()) {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
try {
|
try {
|
||||||
T persistedObject = fileManager.read(storageFile);
|
T persistedObject = fileManager.read(storageFile);
|
||||||
log.trace("Read {} completed in {}msec", serializable.getClass().getSimpleName(), System.currentTimeMillis() - now);
|
log.trace("Read {} completed in {}msec", storageFile, System.currentTimeMillis() - now);
|
||||||
|
|
||||||
// If we did not get any exception we can be sure the data are consistent so we make a backup
|
// If we did not get any exception we can be sure the data are consistent so we make a backup
|
||||||
now = System.currentTimeMillis();
|
now = System.currentTimeMillis();
|
||||||
fileManager.backupFile(fileName);
|
fileManager.backupFile(fileName);
|
||||||
log.trace("Backup {} completed in {}msec", serializable.getClass().getSimpleName(), System.currentTimeMillis() - now);
|
log.trace("Backup {} completed in {}msec", storageFile, System.currentTimeMillis() - now);
|
||||||
|
|
||||||
return persistedObject;
|
return persistedObject;
|
||||||
} catch (ClassCastException | IOException e) {
|
} catch (ClassCastException | IOException e) {
|
||||||
|
|
|
@ -44,6 +44,7 @@ import org.reactfx.util.FxTimer;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
@ -176,7 +177,7 @@ public class OpenOfferManager {
|
||||||
shutDown(null);
|
shutDown(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutDown(Runnable completeHandler) {
|
public void shutDown(@Nullable Runnable completeHandler) {
|
||||||
if (timer != null)
|
if (timer != null)
|
||||||
timer.cancel();
|
timer.cancel();
|
||||||
|
|
||||||
|
@ -188,6 +189,7 @@ public class OpenOfferManager {
|
||||||
offerBookService.removeOfferAtShutDown(openOffer.getOffer());
|
offerBookService.removeOfferAtShutDown(openOffer.getOffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (completeHandler != null)
|
||||||
FxTimer.runLater(Duration.ofMillis(500), completeHandler::run);
|
FxTimer.runLater(Duration.ofMillis(500), completeHandler::run);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,21 +183,23 @@ class MainViewModel implements ViewModel {
|
||||||
private BooleanProperty initP2PNetwork() {
|
private BooleanProperty initP2PNetwork() {
|
||||||
final BooleanProperty initialDataReady = new SimpleBooleanProperty();
|
final BooleanProperty initialDataReady = new SimpleBooleanProperty();
|
||||||
splashP2PNetworkInfo.set("Connecting to Tor network...");
|
splashP2PNetworkInfo.set("Connecting to Tor network...");
|
||||||
|
|
||||||
p2PService.start(new P2PServiceListener() {
|
p2PService.start(new P2PServiceListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onTorNodeReady() {
|
public void onTorNodeReady() {
|
||||||
splashP2PNetworkInfo.set("Publishing Tor Hidden Service...");
|
splashP2PNetworkInfo.set("Publishing Tor Hidden Service...");
|
||||||
|
p2PNetworkInfo.set(splashP2PNetworkInfo.get());
|
||||||
p2PNetworkIconId.set("image-connection-tor");
|
p2PNetworkIconId.set("image-connection-tor");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onHiddenServicePublished() {
|
public void onHiddenServicePublished() {
|
||||||
splashP2PNetworkInfo.set("Authenticating to a seed node...");
|
splashP2PNetworkInfo.set("Authenticating to a seed node...");
|
||||||
|
p2PNetworkInfo.set(splashP2PNetworkInfo.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRequestingDataCompleted() {
|
public void onRequestingDataCompleted() {
|
||||||
p2PNetworkInfo.set("Publishing Tor Hidden Service...");
|
|
||||||
initialDataReady.set(true);
|
initialDataReady.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,14 +342,14 @@ class MainViewModel implements ViewModel {
|
||||||
|
|
||||||
|
|
||||||
// update nr of peers in footer
|
// update nr of peers in footer
|
||||||
p2PService.numAuthenticatedPeers.addListener((observable, oldValue, newValue) -> updateP2pNetworkInfo());
|
p2PService.getNumAuthenticatedPeers().addListener((observable, oldValue, newValue) -> updateP2pNetworkInfo());
|
||||||
|
|
||||||
// now show app
|
// now show app
|
||||||
showAppScreen.set(true);
|
showAppScreen.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateP2pNetworkInfo() {
|
private void updateP2pNetworkInfo() {
|
||||||
p2PNetworkInfo.set("Nr. of authenticated connections: " + p2PService.numAuthenticatedPeers.get());
|
p2PNetworkInfo.set("Nr. of authenticated connections: " + p2PService.getNumAuthenticatedPeers().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void displayAlertIfPresent(Alert alert) {
|
private void displayAlertIfPresent(Alert alert) {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package io.bitsquare.gui.main.market;
|
package io.bitsquare.gui.main.market;
|
||||||
|
|
||||||
|
import io.bitsquare.common.UserThread;
|
||||||
import io.bitsquare.common.util.Tuple2;
|
import io.bitsquare.common.util.Tuple2;
|
||||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||||
import io.bitsquare.gui.common.view.FxmlView;
|
import io.bitsquare.gui.common.view.FxmlView;
|
||||||
|
@ -134,7 +135,8 @@ public class MarketView extends ActivatableViewAndModel<TabPane, MarketViewModel
|
||||||
newValue -> {
|
newValue -> {
|
||||||
String code = newValue.getCode();
|
String code = newValue.getCode();
|
||||||
areaChart.setTitle("Offer book for " + newValue.getName());
|
areaChart.setTitle("Offer book for " + newValue.getName());
|
||||||
xAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(xAxis, "", " " + code + "/BTC"));
|
xAxis.setLabel(priceColumnLabel.get());
|
||||||
|
xAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(xAxis, "", ""));
|
||||||
priceColumnLabel.set("Price (" + code + "/BTC)");
|
priceColumnLabel.set("Price (" + code + "/BTC)");
|
||||||
volumeColumnLabel.set("Volume (" + code + ")");
|
volumeColumnLabel.set("Volume (" + code + ")");
|
||||||
});
|
});
|
||||||
|
@ -156,7 +158,7 @@ public class MarketView extends ActivatableViewAndModel<TabPane, MarketViewModel
|
||||||
TableView<Offer> tableView = new TableView();
|
TableView<Offer> tableView = new TableView();
|
||||||
|
|
||||||
// price
|
// price
|
||||||
TableColumn<Offer, Offer> priceColumn = new TableColumn<>("Price (EUR/BTC)");
|
TableColumn<Offer, Offer> priceColumn = new TableColumn<>();
|
||||||
priceColumn.textProperty().bind(priceColumnLabel);
|
priceColumn.textProperty().bind(priceColumnLabel);
|
||||||
priceColumn.setMinWidth(120);
|
priceColumn.setMinWidth(120);
|
||||||
priceColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
|
priceColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
|
||||||
|
@ -237,7 +239,7 @@ public class MarketView extends ActivatableViewAndModel<TabPane, MarketViewModel
|
||||||
|
|
||||||
Label titleLabel = new Label(direction.equals(Offer.Direction.BUY) ? "Offers for buy bitcoin (bid)" : "Offers for sell bitcoin (ask)");
|
Label titleLabel = new Label(direction.equals(Offer.Direction.BUY) ? "Offers for buy bitcoin (bid)" : "Offers for sell bitcoin (ask)");
|
||||||
titleLabel.setStyle("-fx-font-weight: bold; -fx-font-size: 16; -fx-alignment: center");
|
titleLabel.setStyle("-fx-font-weight: bold; -fx-font-size: 16; -fx-alignment: center");
|
||||||
titleLabel.prefWidthProperty().bind(tableView.widthProperty());
|
UserThread.execute(() -> titleLabel.prefWidthProperty().bind(tableView.widthProperty()));
|
||||||
|
|
||||||
VBox vBox = new VBox();
|
VBox vBox = new VBox();
|
||||||
vBox.setSpacing(10);
|
vBox.setSpacing(10);
|
||||||
|
@ -252,13 +254,13 @@ public class MarketView extends ActivatableViewAndModel<TabPane, MarketViewModel
|
||||||
xAxis = new NumberAxis();
|
xAxis = new NumberAxis();
|
||||||
xAxis.setForceZeroInRange(false);
|
xAxis.setForceZeroInRange(false);
|
||||||
xAxis.setAutoRanging(true);
|
xAxis.setAutoRanging(true);
|
||||||
xAxis.setLabel("Price");
|
xAxis.setLabel(priceColumnLabel.get());
|
||||||
|
|
||||||
yAxis = new NumberAxis();
|
yAxis = new NumberAxis();
|
||||||
yAxis.setForceZeroInRange(false);
|
yAxis.setForceZeroInRange(false);
|
||||||
yAxis.setAutoRanging(true);
|
yAxis.setAutoRanging(true);
|
||||||
yAxis.setLabel("Amount");
|
yAxis.setLabel("Amount in BTC");
|
||||||
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis, "", " BTC"));
|
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis, "", ""));
|
||||||
|
|
||||||
seriesBuy = new XYChart.Series();
|
seriesBuy = new XYChart.Series();
|
||||||
seriesBuy.setName("Offers for buy bitcoin ");
|
seriesBuy.setName("Offers for buy bitcoin ");
|
||||||
|
|
|
@ -127,7 +127,7 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
||||||
}
|
}
|
||||||
|
|
||||||
numAuthenticatedPeersChangeListener = (observable, oldValue, newValue) -> updateAuthenticatedPeersTextArea();
|
numAuthenticatedPeersChangeListener = (observable, oldValue, newValue) -> updateAuthenticatedPeersTextArea();
|
||||||
p2PService.numAuthenticatedPeers.addListener(numAuthenticatedPeersChangeListener);
|
p2PService.getNumAuthenticatedPeers().addListener(numAuthenticatedPeersChangeListener);
|
||||||
updateAuthenticatedPeersTextArea();
|
updateAuthenticatedPeersTextArea();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
||||||
if (p2PServiceListener != null)
|
if (p2PServiceListener != null)
|
||||||
p2PService.removeP2PServiceListener(p2PServiceListener);
|
p2PService.removeP2PServiceListener(p2PServiceListener);
|
||||||
if (numAuthenticatedPeersChangeListener != null)
|
if (numAuthenticatedPeersChangeListener != null)
|
||||||
p2PService.numAuthenticatedPeers.removeListener(numAuthenticatedPeersChangeListener);
|
p2PService.getNumAuthenticatedPeers().removeListener(numAuthenticatedPeersChangeListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateAuthenticatedPeersTextArea() {
|
private void updateAuthenticatedPeersTextArea() {
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
<logger name="org.bitcoinj.core.BitcoinSerializer" level="WARN"/>
|
<logger name="org.bitcoinj.core.BitcoinSerializer" level="WARN"/>
|
||||||
<logger name="org.bitcoinj.core.Peer" level="WARN"/>
|
<logger name="org.bitcoinj.core.Peer" level="WARN"/>
|
||||||
<logger name="org.bitcoinj.core.HeadersMessage" level="WARN"/>
|
<logger name="org.bitcoinj.core.HeadersMessage" level="WARN"/>
|
||||||
<logger name="org.bitcoinj.core.AbstractBlockChain" level="WARN"/>
|
<logger name="org.bitcoinj.core.AbstractBlockChain" level="ERROR"/>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
<logger name="io.netty" level="OFF"/>
|
<logger name="io.netty" level="OFF"/>
|
||||||
|
|
|
@ -13,11 +13,11 @@ public final class SealedAndSignedMessage implements MailboxMessage {
|
||||||
|
|
||||||
private final int networkId = Version.NETWORK_ID;
|
private final int networkId = Version.NETWORK_ID;
|
||||||
public final SealedAndSigned sealedAndSigned;
|
public final SealedAndSigned sealedAndSigned;
|
||||||
public final byte[] blurredAddressHash;
|
public final byte[] addressPrefixHash;
|
||||||
|
|
||||||
public SealedAndSignedMessage(SealedAndSigned sealedAndSigned, byte[] blurredAddressHash) {
|
public SealedAndSignedMessage(SealedAndSigned sealedAndSigned, byte[] addressPrefixHash) {
|
||||||
this.sealedAndSigned = sealedAndSigned;
|
this.sealedAndSigned = sealedAndSigned;
|
||||||
this.blurredAddressHash = blurredAddressHash;
|
this.addressPrefixHash = addressPrefixHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -35,7 +35,7 @@ public final class SealedAndSignedMessage implements MailboxMessage {
|
||||||
return "SealedAndSignedMessage{" +
|
return "SealedAndSignedMessage{" +
|
||||||
"networkId=" + networkId +
|
"networkId=" + networkId +
|
||||||
", sealedAndSigned=" + sealedAndSigned +
|
", sealedAndSigned=" + sealedAndSigned +
|
||||||
", receiverAddressMaskHash.hashCode()=" + Arrays.toString(blurredAddressHash).hashCode() +
|
", receiverAddressMaskHash.hashCode()=" + Arrays.toString(addressPrefixHash).hashCode() +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import java.util.regex.Pattern;
|
||||||
public class Address implements Serializable {
|
public class Address implements Serializable {
|
||||||
public final String hostName;
|
public final String hostName;
|
||||||
public final int port;
|
public final int port;
|
||||||
transient private byte[] blurredAddress;
|
transient private byte[] addressPrefixHash;
|
||||||
|
|
||||||
public Address(String hostName, int port) {
|
public Address(String hostName, int port) {
|
||||||
this.hostName = hostName;
|
this.hostName = hostName;
|
||||||
|
@ -26,10 +26,10 @@ public class Address implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use just a few chars form or address to blur the potential receiver for sent messages
|
// We use just a few chars form or address to blur the potential receiver for sent messages
|
||||||
public byte[] getBlurredAddress() {
|
public byte[] getAddressPrefixHash() {
|
||||||
if (blurredAddress == null)
|
if (addressPrefixHash == null)
|
||||||
blurredAddress = Hash.getHash(getFullAddress().substring(0, 2));
|
addressPrefixHash = Hash.getHash(getFullAddress().substring(0, 2));
|
||||||
return blurredAddress;
|
return addressPrefixHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -16,9 +16,7 @@ import io.bitsquare.crypto.EncryptionService;
|
||||||
import io.bitsquare.crypto.SealedAndSignedMessage;
|
import io.bitsquare.crypto.SealedAndSignedMessage;
|
||||||
import io.bitsquare.p2p.messaging.*;
|
import io.bitsquare.p2p.messaging.*;
|
||||||
import io.bitsquare.p2p.network.*;
|
import io.bitsquare.p2p.network.*;
|
||||||
import io.bitsquare.p2p.peers.Peer;
|
|
||||||
import io.bitsquare.p2p.peers.PeerGroup;
|
import io.bitsquare.p2p.peers.PeerGroup;
|
||||||
import io.bitsquare.p2p.peers.PeerListener;
|
|
||||||
import io.bitsquare.p2p.seed.SeedNodesRepository;
|
import io.bitsquare.p2p.seed.SeedNodesRepository;
|
||||||
import io.bitsquare.p2p.storage.HashMapChangedListener;
|
import io.bitsquare.p2p.storage.HashMapChangedListener;
|
||||||
import io.bitsquare.p2p.storage.ProtectedExpirableDataStorage;
|
import io.bitsquare.p2p.storage.ProtectedExpirableDataStorage;
|
||||||
|
@ -26,13 +24,10 @@ import io.bitsquare.p2p.storage.data.ExpirableMailboxPayload;
|
||||||
import io.bitsquare.p2p.storage.data.ExpirablePayload;
|
import io.bitsquare.p2p.storage.data.ExpirablePayload;
|
||||||
import io.bitsquare.p2p.storage.data.ProtectedData;
|
import io.bitsquare.p2p.storage.data.ProtectedData;
|
||||||
import io.bitsquare.p2p.storage.data.ProtectedMailboxData;
|
import io.bitsquare.p2p.storage.data.ProtectedMailboxData;
|
||||||
import io.bitsquare.p2p.storage.messages.DataExchangeMessage;
|
|
||||||
import io.bitsquare.p2p.storage.messages.GetDataRequest;
|
import io.bitsquare.p2p.storage.messages.GetDataRequest;
|
||||||
import io.bitsquare.p2p.storage.messages.GetDataResponse;
|
import io.bitsquare.p2p.storage.messages.GetDataResponse;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import io.bitsquare.storage.Storage;
|
||||||
import javafx.beans.property.IntegerProperty;
|
import javafx.beans.property.*;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
|
||||||
import javafx.beans.property.SimpleIntegerProperty;
|
|
||||||
import org.fxmisc.easybind.EasyBind;
|
import org.fxmisc.easybind.EasyBind;
|
||||||
import org.fxmisc.easybind.monadic.MonadicBinding;
|
import org.fxmisc.easybind.monadic.MonadicBinding;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
@ -52,11 +47,9 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
/**
|
/**
|
||||||
* Represents our node in the P2P network
|
* Represents our node in the P2P network
|
||||||
*/
|
*/
|
||||||
public class P2PService implements SetupListener, MessageListener, ConnectionListener, PeerListener {
|
public class P2PService implements SetupListener, MessageListener, ConnectionListener {
|
||||||
private static final Logger log = LoggerFactory.getLogger(P2PService.class);
|
private static final Logger log = LoggerFactory.getLogger(P2PService.class);
|
||||||
|
|
||||||
private static final int RETRY_GET_DATA = 10 * 1000;
|
|
||||||
|
|
||||||
private final SeedNodesRepository seedNodesRepository;
|
private final SeedNodesRepository seedNodesRepository;
|
||||||
private final int port;
|
private final int port;
|
||||||
private final File torDir;
|
private final File torDir;
|
||||||
|
@ -64,25 +57,29 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
@Nullable
|
@Nullable
|
||||||
private final EncryptionService encryptionService;
|
private final EncryptionService encryptionService;
|
||||||
private final KeyRing keyRing;
|
private final KeyRing keyRing;
|
||||||
private final File storageDir;
|
|
||||||
|
|
||||||
|
// set in init
|
||||||
private NetworkNode networkNode;
|
private NetworkNode networkNode;
|
||||||
private PeerGroup peerGroup;
|
private PeerGroup peerGroup;
|
||||||
private ProtectedExpirableDataStorage dataStorage;
|
private ProtectedExpirableDataStorage dataStorage;
|
||||||
|
|
||||||
private final CopyOnWriteArraySet<DecryptedMailListener> decryptedMailListeners = new CopyOnWriteArraySet<>();
|
private final CopyOnWriteArraySet<DecryptedMailListener> decryptedMailListeners = new CopyOnWriteArraySet<>();
|
||||||
private final CopyOnWriteArraySet<DecryptedMailboxListener> decryptedMailboxListeners = new CopyOnWriteArraySet<>();
|
private final CopyOnWriteArraySet<DecryptedMailboxListener> decryptedMailboxListeners = new CopyOnWriteArraySet<>();
|
||||||
private final CopyOnWriteArraySet<P2PServiceListener> p2pServiceListeners = new CopyOnWriteArraySet<>();
|
private final CopyOnWriteArraySet<P2PServiceListener> p2pServiceListeners = new CopyOnWriteArraySet<>();
|
||||||
private final Map<DecryptedMsgWithPubKey, ProtectedMailboxData> mailboxMap = new HashMap<>();
|
private final Map<DecryptedMsgWithPubKey, ProtectedMailboxData> mailboxMap = new HashMap<>();
|
||||||
private volatile boolean shutDownInProgress;
|
|
||||||
private Address connectedSeedNode;
|
|
||||||
private final Set<Address> authenticatedPeerAddresses = new HashSet<>();
|
private final Set<Address> authenticatedPeerAddresses = new HashSet<>();
|
||||||
private boolean shutDownComplete;
|
|
||||||
private final CopyOnWriteArraySet<Runnable> shutDownResultHandlers = new CopyOnWriteArraySet<>();
|
private final CopyOnWriteArraySet<Runnable> shutDownResultHandlers = new CopyOnWriteArraySet<>();
|
||||||
private final BooleanProperty hiddenServicePublished = new SimpleBooleanProperty();
|
private final BooleanProperty hiddenServicePublished = new SimpleBooleanProperty();
|
||||||
private final BooleanProperty requestingDataCompleted = new SimpleBooleanProperty();
|
private final BooleanProperty requestingDataCompleted = new SimpleBooleanProperty();
|
||||||
private final BooleanProperty authenticated = new SimpleBooleanProperty();
|
private final BooleanProperty authenticated = new SimpleBooleanProperty();
|
||||||
|
private final IntegerProperty numAuthenticatedPeers = new SimpleIntegerProperty(0);
|
||||||
|
|
||||||
|
private Address connectedSeedNode;
|
||||||
|
private volatile boolean shutDownInProgress;
|
||||||
|
private boolean shutDownComplete;
|
||||||
private MonadicBinding<Boolean> readyForAuthentication;
|
private MonadicBinding<Boolean> readyForAuthentication;
|
||||||
public final IntegerProperty numAuthenticatedPeers = new SimpleIntegerProperty(0);
|
private final Storage<Address> dbStorage;
|
||||||
|
private Address myOnionAddress;
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -105,40 +102,42 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
this.useLocalhost = useLocalhost;
|
this.useLocalhost = useLocalhost;
|
||||||
this.encryptionService = encryptionService;
|
this.encryptionService = encryptionService;
|
||||||
this.keyRing = keyRing;
|
this.keyRing = keyRing;
|
||||||
this.storageDir = storageDir;
|
dbStorage = new Storage<>(storageDir);
|
||||||
|
|
||||||
init(networkId);
|
init(networkId, storageDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init(int networkId) {
|
private void init(int networkId, File storageDir) {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
|
|
||||||
|
Address persisted = dbStorage.initAndGetPersisted("myOnionAddress");
|
||||||
|
if (persisted != null)
|
||||||
|
this.myOnionAddress = persisted;
|
||||||
|
|
||||||
// network
|
// network
|
||||||
networkNode = useLocalhost ? new LocalhostNetworkNode(port) : new TorNetworkNode(port, torDir);
|
networkNode = useLocalhost ? new LocalhostNetworkNode(port) : new TorNetworkNode(port, torDir);
|
||||||
Set<Address> seedNodeAddresses = seedNodesRepository.geSeedNodeAddresses(useLocalhost, networkId);
|
Set<Address> seedNodeAddresses = seedNodesRepository.geSeedNodeAddresses(useLocalhost, networkId);
|
||||||
|
|
||||||
// peer group
|
// peer group
|
||||||
peerGroup = new PeerGroup(networkNode, seedNodeAddresses);
|
peerGroup = new PeerGroup(networkNode, seedNodeAddresses);
|
||||||
if (useLocalhost) PeerGroup.setSimulateAuthTorNode(2 * 100);
|
if (useLocalhost)
|
||||||
|
PeerGroup.setSimulateAuthTorNode(400);
|
||||||
|
|
||||||
// storage
|
// P2P network storage
|
||||||
dataStorage = new ProtectedExpirableDataStorage(peerGroup, storageDir);
|
dataStorage = new ProtectedExpirableDataStorage(peerGroup, storageDir);
|
||||||
|
|
||||||
|
|
||||||
networkNode.addConnectionListener(this);
|
networkNode.addConnectionListener(this);
|
||||||
networkNode.addMessageListener(this);
|
networkNode.addMessageListener(this);
|
||||||
peerGroup.addPeerListener(this);
|
|
||||||
|
|
||||||
dataStorage.addHashMapChangedListener(new HashMapChangedListener() {
|
dataStorage.addHashMapChangedListener(new HashMapChangedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onAdded(ProtectedData entry) {
|
public void onAdded(ProtectedData entry) {
|
||||||
Log.traceCall();
|
|
||||||
if (entry instanceof ProtectedMailboxData)
|
if (entry instanceof ProtectedMailboxData)
|
||||||
tryDecryptMailboxData((ProtectedMailboxData) entry);
|
processProtectedMailboxData((ProtectedMailboxData) entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRemoved(ProtectedData entry) {
|
public void onRemoved(ProtectedData entry) {
|
||||||
Log.traceCall();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -148,7 +147,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
// we need to have both the initial data delivered and the hidden service published before we
|
// we need to have both the initial data delivered and the hidden service published before we
|
||||||
// bootstrap and authenticate to other nodes.
|
// bootstrap and authenticate to other nodes.
|
||||||
if (newValue)
|
if (newValue)
|
||||||
tryAuthenticateSeedNode();
|
authenticateSeedNode();
|
||||||
});
|
});
|
||||||
|
|
||||||
requestingDataCompleted.addListener((observable, oldValue, newValue) -> {
|
requestingDataCompleted.addListener((observable, oldValue, newValue) -> {
|
||||||
|
@ -173,21 +172,22 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
// we keep that connection open as the bootstrapping peer will use that for the authentication
|
// we keep that connection open as the bootstrapping peer will use that for the authentication
|
||||||
// as we are not authenticated yet the data adding will not be broadcasted
|
// as we are not authenticated yet the data adding will not be broadcasted
|
||||||
set.stream().forEach(e -> dataStorage.add(e, connection.getPeerAddress()));
|
set.stream().forEach(e -> dataStorage.add(e, connection.getPeerAddress()));
|
||||||
setRequestingDataCompleted();
|
onRequestingDataComplete();
|
||||||
} else if (message instanceof DataExchangeMessage) {
|
|
||||||
Log.traceCall(message.toString());
|
|
||||||
DataExchangeMessage dataExchangeMessage = (DataExchangeMessage) message;
|
|
||||||
HashSet<ProtectedData> set = dataExchangeMessage.set;
|
|
||||||
set.stream().forEach(e -> dataStorage.add(e, connection.getPeerAddress()));
|
|
||||||
setRequestingDataCompleted();
|
|
||||||
} else if (message instanceof SealedAndSignedMessage) {
|
} else if (message instanceof SealedAndSignedMessage) {
|
||||||
Log.traceCall(message.toString());
|
Log.traceCall(message.toString());
|
||||||
|
// Seed nodes don't have set the encryptionService
|
||||||
if (encryptionService != null) {
|
if (encryptionService != null) {
|
||||||
try {
|
try {
|
||||||
SealedAndSignedMessage sealedAndSignedMessage = (SealedAndSignedMessage) message;
|
SealedAndSignedMessage sealedAndSignedMessage = (SealedAndSignedMessage) message;
|
||||||
if (verifyBlurredAddressHash(sealedAndSignedMessage)) {
|
if (verifyAddressPrefixHash(sealedAndSignedMessage)) {
|
||||||
DecryptedMsgWithPubKey decryptedMsgWithPubKey = encryptionService.decryptAndVerify(
|
DecryptedMsgWithPubKey decryptedMsgWithPubKey = encryptionService.decryptAndVerify(
|
||||||
sealedAndSignedMessage.sealedAndSigned);
|
sealedAndSignedMessage.sealedAndSigned);
|
||||||
|
|
||||||
|
// We set connectionType to that connection to avoid that is get closed when
|
||||||
|
// we get too many connection attempts.
|
||||||
|
// That is used as protection against eclipse attacks.
|
||||||
|
connection.setConnectionType(ConnectionsType.DIRECT_MSG);
|
||||||
|
|
||||||
log.info("Received SealedAndSignedMessage and decrypted it: " + decryptedMsgWithPubKey);
|
log.info("Received SealedAndSignedMessage and decrypted it: " + decryptedMsgWithPubKey);
|
||||||
decryptedMailListeners.stream().forEach(
|
decryptedMailListeners.stream().forEach(
|
||||||
e -> e.onMailMessage(decryptedMsgWithPubKey, connection.getPeerAddress()));
|
e -> e.onMailMessage(decryptedMsgWithPubKey, connection.getPeerAddress()));
|
||||||
|
@ -202,12 +202,6 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean verifyBlurredAddressHash(SealedAndSignedMessage sealedAndSignedMessage) {
|
|
||||||
byte[] blurredAddressHash = getAddress().getBlurredAddress();
|
|
||||||
return blurredAddressHash != null &&
|
|
||||||
Arrays.equals(blurredAddressHash, sealedAndSignedMessage.blurredAddressHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// ConnectionListener implementation
|
// ConnectionListener implementation
|
||||||
|
@ -223,9 +217,12 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
checkArgument(peerAddress.equals(connection.getPeerAddress()),
|
checkArgument(peerAddress.equals(connection.getPeerAddress()),
|
||||||
"peerAddress must match connection.getPeerAddress()");
|
"peerAddress must match connection.getPeerAddress()");
|
||||||
authenticatedPeerAddresses.add(peerAddress);
|
authenticatedPeerAddresses.add(peerAddress);
|
||||||
authenticated.set(true);
|
|
||||||
|
|
||||||
|
if (!authenticated.get()) {
|
||||||
|
authenticated.set(true);
|
||||||
|
sendGetDataRequestAfterAuthentication(peerAddress, connection);
|
||||||
p2pServiceListeners.stream().forEach(e -> e.onFirstPeerAuthenticated());
|
p2pServiceListeners.stream().forEach(e -> e.onFirstPeerAuthenticated());
|
||||||
|
}
|
||||||
|
|
||||||
numAuthenticatedPeers.set(authenticatedPeerAddresses.size());
|
numAuthenticatedPeers.set(authenticatedPeerAddresses.size());
|
||||||
}
|
}
|
||||||
|
@ -244,31 +241,6 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// PeerListener implementation
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFirstAuthenticatePeer(Peer peer) {
|
|
||||||
Log.traceCall();
|
|
||||||
log.trace("onFirstAuthenticatePeer " + peer);
|
|
||||||
sendGetAllDataMessageAfterAuthentication(peer);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPeerAdded(Peer peer) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPeerRemoved(Address address) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConnectionAuthenticated(Connection connection) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// SetupListener implementation
|
// SetupListener implementation
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -283,10 +255,60 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
sendGetDataRequest(peerGroup.getSeedNodeAddresses());
|
sendGetDataRequest(peerGroup.getSeedNodeAddresses());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendGetDataRequest(Collection<Address> seedNodeAddresses) {
|
||||||
|
Log.traceCall(seedNodeAddresses.toString());
|
||||||
|
if (!seedNodeAddresses.isEmpty()) {
|
||||||
|
List<Address> remainingSeedNodeAddresses = new ArrayList<>(seedNodeAddresses);
|
||||||
|
Collections.shuffle(remainingSeedNodeAddresses);
|
||||||
|
Address candidate = remainingSeedNodeAddresses.remove(0);
|
||||||
|
log.info("We try to send a GetAllDataMessage request to a random seed node. " + candidate);
|
||||||
|
|
||||||
|
SettableFuture<Connection> future = networkNode.sendMessage(candidate, new GetDataRequest());
|
||||||
|
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(@Nullable Connection connection) {
|
||||||
|
log.info("Send GetAllDataMessage to " + candidate + " succeeded.");
|
||||||
|
checkArgument(connectedSeedNode == null, "We have already a connectedSeedNode. That should not happen.");
|
||||||
|
connectedSeedNode = candidate;
|
||||||
|
|
||||||
|
// In case we get called from a retry we check if we need to authenticate
|
||||||
|
if (!authenticated.get() && hiddenServicePublished.get())
|
||||||
|
authenticateSeedNode();
|
||||||
|
else
|
||||||
|
log.debug("No connected seedNode available.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NotNull Throwable throwable) {
|
||||||
|
log.info("Send GetAllDataMessage to " + candidate + " failed. " +
|
||||||
|
"That is expected if other seed nodes are offline. " +
|
||||||
|
"Exception:" + throwable.getMessage());
|
||||||
|
if (!remainingSeedNodeAddresses.isEmpty())
|
||||||
|
log.trace("We try to connect another random seed node. " + remainingSeedNodeAddresses);
|
||||||
|
|
||||||
|
sendGetDataRequest(remainingSeedNodeAddresses);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
log.info("There is no seed node available for requesting data. " +
|
||||||
|
"That is expected if no seed node is online.\n" +
|
||||||
|
"We will try again after a bit ");
|
||||||
|
onRequestingDataComplete();
|
||||||
|
|
||||||
|
UserThread.runAfterRandomDelay(() -> sendGetDataRequest(peerGroup.getSeedNodeAddresses()),
|
||||||
|
20, 30, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onHiddenServicePublished() {
|
public void onHiddenServicePublished() {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
checkArgument(networkNode.getAddress() != null, "Address must be set when we have the hidden service ready");
|
checkArgument(networkNode.getAddress() != null, "Address must be set when we have the hidden service ready");
|
||||||
|
if (myOnionAddress != null)
|
||||||
|
checkArgument(networkNode.getAddress() == myOnionAddress, "networkNode.getAddress() must be same as myOnionAddress");
|
||||||
|
|
||||||
|
myOnionAddress = networkNode.getAddress();
|
||||||
|
dbStorage.queueUpForSave(myOnionAddress);
|
||||||
|
|
||||||
p2pServiceListeners.stream().forEach(e -> e.onHiddenServicePublished());
|
p2pServiceListeners.stream().forEach(e -> e.onHiddenServicePublished());
|
||||||
|
|
||||||
|
@ -300,83 +322,37 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
p2pServiceListeners.stream().forEach(e -> e.onSetupFailed(throwable));
|
p2pServiceListeners.stream().forEach(e -> e.onSetupFailed(throwable));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendGetDataRequest(Collection<Address> seedNodeAddresses) {
|
private void onRequestingDataComplete() {
|
||||||
Log.traceCall();
|
|
||||||
if (!seedNodeAddresses.isEmpty()) {
|
|
||||||
List<Address> remainingSeedNodeAddresses = new ArrayList<>(seedNodeAddresses);
|
|
||||||
Collections.shuffle(remainingSeedNodeAddresses);
|
|
||||||
Address candidate = remainingSeedNodeAddresses.remove(0);
|
|
||||||
log.info("We try to send a GetAllDataMessage request to a random seed node. " + candidate);
|
|
||||||
|
|
||||||
SettableFuture<Connection> future = networkNode.sendMessage(candidate, new GetDataRequest());
|
|
||||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(@Nullable Connection connection) {
|
|
||||||
log.info("Send GetAllDataMessage to " + candidate + " succeeded.");
|
|
||||||
connectedSeedNode = candidate;
|
|
||||||
|
|
||||||
// In case we get called by a retry we check if we need authenticate as well
|
|
||||||
if (hiddenServicePublished.get() && !authenticated.get()) {
|
|
||||||
peerGroup.authenticateSeedNode(connectedSeedNode);
|
|
||||||
} else {
|
|
||||||
log.debug("No connected seedNode available.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(@NotNull Throwable throwable) {
|
|
||||||
log.info("Send GetAllDataMessage to " + candidate + " failed. " +
|
|
||||||
"That is expected if other seed nodes are offline." +
|
|
||||||
"\nException:" + throwable.getMessage());
|
|
||||||
if (!remainingSeedNodeAddresses.isEmpty())
|
|
||||||
log.trace("We try to connect another random seed node. " + remainingSeedNodeAddresses);
|
|
||||||
|
|
||||||
sendGetDataRequest(remainingSeedNodeAddresses);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
log.info("There is no seed node available for requesting data. " +
|
|
||||||
"That is expected if no seed node is available.\n" +
|
|
||||||
"We will try again after {} ms", RETRY_GET_DATA);
|
|
||||||
setRequestingDataCompleted();
|
|
||||||
|
|
||||||
UserThread.runAfter(() -> sendGetDataRequest(peerGroup.getSeedNodeAddresses()),
|
|
||||||
RETRY_GET_DATA, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setRequestingDataCompleted() {
|
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
// 2. (or 3.) Step: We got all data loaded (or no seed node available - should not happen in real operation)
|
// 2. (or 3.) Step: We got all data loaded (or no seed node available - should not happen in real operation)
|
||||||
requestingDataCompleted.set(true);
|
requestingDataCompleted.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Step: hiddenServicePublished and allDataLoaded. We start authenticate to the connected seed node.
|
// 4. Step: hiddenServicePublished and allDataLoaded. We start authenticate to the connected seed node.
|
||||||
private void tryAuthenticateSeedNode() {
|
private void authenticateSeedNode() {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
if (connectedSeedNode != null) {
|
checkNotNull(connectedSeedNode != null, "connectedSeedNode must not be null");
|
||||||
|
if (connectedSeedNode != null)
|
||||||
peerGroup.authenticateSeedNode(connectedSeedNode);
|
peerGroup.authenticateSeedNode(connectedSeedNode);
|
||||||
} else {
|
|
||||||
log.debug("No connected seedNode available.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Step:
|
// 5. Step:
|
||||||
private void sendGetAllDataMessageAfterAuthentication(final Peer peer) {
|
private void sendGetDataRequestAfterAuthentication(Address peerAddress, Connection connection) {
|
||||||
Log.traceCall(peer.toString());
|
Log.traceCall(peerAddress.toString());
|
||||||
// We have to exchange the data again as we might have missed pushed data in the meantime
|
// We have to exchange the data again as we might have missed pushed data in the meantime
|
||||||
// After authentication we send our data set to the other peer.
|
// After authentication we send our data set to the other peer.
|
||||||
// As he will do the same we will get his actual data set.
|
// As he will do the same we will get his actual data set.
|
||||||
SettableFuture<Connection> future = networkNode.sendMessage(peer.connection, new DataExchangeMessage(getDataSet()));
|
SettableFuture<Connection> future = networkNode.sendMessage(connection, new GetDataRequest());
|
||||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(@Nullable Connection connection) {
|
public void onSuccess(@Nullable Connection connection) {
|
||||||
log.info("sendGetAllDataMessageAfterAuthentication Send DataExchangeMessage to " + peer.address + " succeeded.");
|
log.trace("sendGetDataRequestAfterAuthentication: Send GetDataRequest to " + peerAddress + " succeeded.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NotNull Throwable throwable) {
|
public void onFailure(@NotNull Throwable throwable) {
|
||||||
log.warn("sendGetAllDataMessageAfterAuthentication Send DataExchangeMessage to " + peer.address + " failed. " +
|
//TODO how to deal with that case?
|
||||||
|
log.warn("sendGetDataRequestAfterAuthentication: Send GetDataRequest to " + peerAddress + " failed. " +
|
||||||
"Exception:" + throwable.getMessage());
|
"Exception:" + throwable.getMessage());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -429,13 +405,13 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
shutDownCompleteHandler.run();
|
shutDownCompleteHandler.run();
|
||||||
else
|
else
|
||||||
shutDownResultHandlers.add(shutDownCompleteHandler);
|
shutDownResultHandlers.add(shutDownCompleteHandler);
|
||||||
log.warn("shutDown already in progress");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
log.debug("shutDown already in progress");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Messaging
|
// MailMessages
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void sendEncryptedMailMessage(Address peerAddress, PubKeyRing pubKeyRing, MailMessage message,
|
public void sendEncryptedMailMessage(Address peerAddress, PubKeyRing pubKeyRing, MailMessage message,
|
||||||
|
@ -445,7 +421,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
checkAuthentication();
|
checkAuthentication();
|
||||||
|
|
||||||
if (!authenticatedPeerAddresses.contains(peerAddress))
|
if (!authenticatedPeerAddresses.contains(peerAddress))
|
||||||
peerGroup.authenticateToPeer(peerAddress,
|
peerGroup.authenticateToDirectMessagePeer(peerAddress,
|
||||||
() -> doSendEncryptedMailMessage(peerAddress, pubKeyRing, message, sendMailMessageListener),
|
() -> doSendEncryptedMailMessage(peerAddress, pubKeyRing, message, sendMailMessageListener),
|
||||||
() -> sendMailMessageListener.onFault());
|
() -> sendMailMessageListener.onFault());
|
||||||
else
|
else
|
||||||
|
@ -458,7 +434,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
if (encryptionService != null) {
|
if (encryptionService != null) {
|
||||||
try {
|
try {
|
||||||
SealedAndSignedMessage sealedAndSignedMessage = new SealedAndSignedMessage(
|
SealedAndSignedMessage sealedAndSignedMessage = new SealedAndSignedMessage(
|
||||||
encryptionService.encryptAndSign(pubKeyRing, message), peerAddress.getBlurredAddress());
|
encryptionService.encryptAndSign(pubKeyRing, message), peerAddress.getAddressPrefixHash());
|
||||||
SettableFuture<Connection> future = networkNode.sendMessage(peerAddress, sealedAndSignedMessage);
|
SettableFuture<Connection> future = networkNode.sendMessage(peerAddress, sealedAndSignedMessage);
|
||||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -479,6 +455,47 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// MailboxMessages
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private void processProtectedMailboxData(ProtectedMailboxData mailboxData) {
|
||||||
|
if (encryptionService != null) {
|
||||||
|
Log.traceCall();
|
||||||
|
ExpirablePayload expirablePayload = mailboxData.expirablePayload;
|
||||||
|
if (expirablePayload instanceof ExpirableMailboxPayload) {
|
||||||
|
ExpirableMailboxPayload expirableMailboxPayload = (ExpirableMailboxPayload) expirablePayload;
|
||||||
|
SealedAndSignedMessage sealedAndSignedMessage = expirableMailboxPayload.sealedAndSignedMessage;
|
||||||
|
if (verifyAddressPrefixHash(sealedAndSignedMessage)) {
|
||||||
|
try {
|
||||||
|
DecryptedMsgWithPubKey decryptedMsgWithPubKey = encryptionService.decryptAndVerify(
|
||||||
|
sealedAndSignedMessage.sealedAndSigned);
|
||||||
|
if (decryptedMsgWithPubKey.message instanceof MailboxMessage) {
|
||||||
|
MailboxMessage mailboxMessage = (MailboxMessage) decryptedMsgWithPubKey.message;
|
||||||
|
Address senderAddress = mailboxMessage.getSenderAddress();
|
||||||
|
checkNotNull(senderAddress, "senderAddress must not be null for mailbox messages");
|
||||||
|
|
||||||
|
mailboxMap.put(decryptedMsgWithPubKey, mailboxData);
|
||||||
|
log.trace("Decryption of SealedAndSignedMessage succeeded. senderAddress="
|
||||||
|
+ senderAddress + " / my address=" + getAddress());
|
||||||
|
decryptedMailboxListeners.stream().forEach(
|
||||||
|
e -> e.onMailboxMessageAdded(decryptedMsgWithPubKey, senderAddress));
|
||||||
|
} else {
|
||||||
|
log.warn("tryDecryptMailboxData: Expected MailboxMessage but got other type. " +
|
||||||
|
"decryptedMsgWithPubKey.message=", decryptedMsgWithPubKey.message);
|
||||||
|
}
|
||||||
|
} catch (CryptoException e) {
|
||||||
|
log.trace("Decryption of SealedAndSignedMessage failed. " +
|
||||||
|
"That is expected if the message is not intended for us. " + e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.info("Wrong blurredAddressHash. The message is not intended for us.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void sendEncryptedMailboxMessage(Address peerAddress, PubKeyRing peersPubKeyRing,
|
public void sendEncryptedMailboxMessage(Address peerAddress, PubKeyRing peersPubKeyRing,
|
||||||
MailboxMessage message, SendMailboxMessageListener sendMailboxMessageListener) {
|
MailboxMessage message, SendMailboxMessageListener sendMailboxMessageListener) {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
|
@ -489,7 +506,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
if (authenticatedPeerAddresses.contains(peerAddress)) {
|
if (authenticatedPeerAddresses.contains(peerAddress)) {
|
||||||
trySendEncryptedMailboxMessage(peerAddress, peersPubKeyRing, message, sendMailboxMessageListener);
|
trySendEncryptedMailboxMessage(peerAddress, peersPubKeyRing, message, sendMailboxMessageListener);
|
||||||
} else {
|
} else {
|
||||||
peerGroup.authenticateToPeer(peerAddress,
|
peerGroup.authenticateToDirectMessagePeer(peerAddress,
|
||||||
() -> trySendEncryptedMailboxMessage(peerAddress, peersPubKeyRing, message, sendMailboxMessageListener),
|
() -> trySendEncryptedMailboxMessage(peerAddress, peersPubKeyRing, message, sendMailboxMessageListener),
|
||||||
() -> {
|
() -> {
|
||||||
log.info("We cannot authenticate to peer. Peer might be offline. We will store message in mailbox.");
|
log.info("We cannot authenticate to peer. Peer might be offline. We will store message in mailbox.");
|
||||||
|
@ -498,13 +515,14 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// send message and if it fails (peer offline) we store the data to the network
|
||||||
private void trySendEncryptedMailboxMessage(Address peerAddress, PubKeyRing peersPubKeyRing,
|
private void trySendEncryptedMailboxMessage(Address peerAddress, PubKeyRing peersPubKeyRing,
|
||||||
MailboxMessage message, SendMailboxMessageListener sendMailboxMessageListener) {
|
MailboxMessage message, SendMailboxMessageListener sendMailboxMessageListener) {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
if (encryptionService != null) {
|
if (encryptionService != null) {
|
||||||
try {
|
try {
|
||||||
SealedAndSignedMessage sealedAndSignedMessage = new SealedAndSignedMessage(
|
SealedAndSignedMessage sealedAndSignedMessage = new SealedAndSignedMessage(
|
||||||
encryptionService.encryptAndSign(peersPubKeyRing, message), peerAddress.getBlurredAddress());
|
encryptionService.encryptAndSign(peersPubKeyRing, message), peerAddress.getAddressPrefixHash());
|
||||||
SettableFuture<Connection> future = networkNode.sendMessage(peerAddress, sealedAndSignedMessage);
|
SettableFuture<Connection> future = networkNode.sendMessage(peerAddress, sealedAndSignedMessage);
|
||||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -535,7 +553,6 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Data storage
|
// Data storage
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -545,8 +562,8 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
checkAuthentication();
|
checkAuthentication();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return dataStorage.add(dataStorage.getDataWithSignedSeqNr(expirablePayload,
|
ProtectedData protectedData = dataStorage.getDataWithSignedSeqNr(expirablePayload, keyRing.getSignatureKeyPair());
|
||||||
keyRing.getSignatureKeyPair()), networkNode.getAddress());
|
return dataStorage.add(protectedData, networkNode.getAddress());
|
||||||
} catch (CryptoException e) {
|
} catch (CryptoException e) {
|
||||||
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
||||||
return false;
|
return false;
|
||||||
|
@ -558,8 +575,11 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
checkAuthentication();
|
checkAuthentication();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
dataStorage.add(dataStorage.getMailboxDataWithSignedSeqNr(expirableMailboxPayload,
|
ProtectedMailboxData protectedMailboxData = dataStorage.getMailboxDataWithSignedSeqNr(
|
||||||
keyRing.getSignatureKeyPair(), receiversPublicKey), networkNode.getAddress());
|
expirableMailboxPayload,
|
||||||
|
keyRing.getSignatureKeyPair(),
|
||||||
|
receiversPublicKey);
|
||||||
|
dataStorage.add(protectedMailboxData, networkNode.getAddress());
|
||||||
} catch (CryptoException e) {
|
} catch (CryptoException e) {
|
||||||
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
||||||
}
|
}
|
||||||
|
@ -570,8 +590,8 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
checkAuthentication();
|
checkAuthentication();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return dataStorage.remove(dataStorage.getDataWithSignedSeqNr(expirablePayload,
|
ProtectedData protectedData = dataStorage.getDataWithSignedSeqNr(expirablePayload, keyRing.getSignatureKeyPair());
|
||||||
keyRing.getSignatureKeyPair()), networkNode.getAddress());
|
return dataStorage.remove(protectedData, networkNode.getAddress());
|
||||||
} catch (CryptoException e) {
|
} catch (CryptoException e) {
|
||||||
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
||||||
return false;
|
return false;
|
||||||
|
@ -585,12 +605,22 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
if (mailboxMap.containsKey(decryptedMsgWithPubKey)) {
|
if (mailboxMap.containsKey(decryptedMsgWithPubKey)) {
|
||||||
ProtectedMailboxData mailboxData = mailboxMap.get(decryptedMsgWithPubKey);
|
ProtectedMailboxData mailboxData = mailboxMap.get(decryptedMsgWithPubKey);
|
||||||
if (mailboxData != null && mailboxData.expirablePayload instanceof ExpirableMailboxPayload) {
|
if (mailboxData != null && mailboxData.expirablePayload instanceof ExpirableMailboxPayload) {
|
||||||
checkArgument(mailboxData.receiversPubKey.equals(keyRing.getSignatureKeyPair().getPublic()),
|
ExpirableMailboxPayload expirableMailboxPayload = (ExpirableMailboxPayload) mailboxData.expirablePayload;
|
||||||
"mailboxData.receiversPubKey is not matching with our key. That must not happen.");
|
PublicKey receiversPubKey = mailboxData.receiversPubKey;
|
||||||
removeMailboxData((ExpirableMailboxPayload) mailboxData.expirablePayload, mailboxData.receiversPubKey);
|
checkArgument(receiversPubKey.equals(keyRing.getSignatureKeyPair().getPublic()),
|
||||||
mailboxMap.remove(decryptedMsgWithPubKey);
|
"receiversPubKey is not matching with our key. That must not happen.");
|
||||||
log.trace("Removed successfully protectedExpirableData.");
|
try {
|
||||||
|
ProtectedMailboxData protectedMailboxData = dataStorage.getMailboxDataWithSignedSeqNr(
|
||||||
|
expirableMailboxPayload,
|
||||||
|
keyRing.getSignatureKeyPair(),
|
||||||
|
receiversPubKey);
|
||||||
|
dataStorage.removeMailboxData(protectedMailboxData, networkNode.getAddress());
|
||||||
|
} catch (CryptoException e) {
|
||||||
|
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
||||||
|
}
|
||||||
|
|
||||||
|
mailboxMap.remove(decryptedMsgWithPubKey);
|
||||||
|
log.trace("Removed successfully decryptedMsgWithPubKey.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.warn("decryptedMsgWithPubKey not found in mailboxMap. That should never happen." +
|
log.warn("decryptedMsgWithPubKey not found in mailboxMap. That should never happen." +
|
||||||
|
@ -598,20 +628,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeMailboxData(ExpirableMailboxPayload expirableMailboxPayload, PublicKey receiversPublicKey) {
|
|
||||||
Log.traceCall();
|
|
||||||
checkAuthentication();
|
|
||||||
|
|
||||||
try {
|
|
||||||
dataStorage.removeMailboxData(dataStorage.getMailboxDataWithSignedSeqNr(expirableMailboxPayload,
|
|
||||||
keyRing.getSignatureKeyPair(), receiversPublicKey), networkNode.getAddress());
|
|
||||||
} catch (CryptoException e) {
|
|
||||||
log.error("Signing at getDataWithSignedSeqNr failed. That should never happen.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<ByteArray, ProtectedData> getDataMap() {
|
public Map<ByteArray, ProtectedData> getDataMap() {
|
||||||
Log.traceCall();
|
|
||||||
return dataStorage.getMap();
|
return dataStorage.getMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -621,37 +638,30 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void addDecryptedMailListener(DecryptedMailListener listener) {
|
public void addDecryptedMailListener(DecryptedMailListener listener) {
|
||||||
Log.traceCall();
|
|
||||||
decryptedMailListeners.add(listener);
|
decryptedMailListeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeDecryptedMailListener(DecryptedMailListener listener) {
|
public void removeDecryptedMailListener(DecryptedMailListener listener) {
|
||||||
Log.traceCall();
|
|
||||||
decryptedMailListeners.remove(listener);
|
decryptedMailListeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDecryptedMailboxListener(DecryptedMailboxListener listener) {
|
public void addDecryptedMailboxListener(DecryptedMailboxListener listener) {
|
||||||
Log.traceCall();
|
|
||||||
decryptedMailboxListeners.add(listener);
|
decryptedMailboxListeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeDecryptedMailboxListener(DecryptedMailboxListener listener) {
|
public void removeDecryptedMailboxListener(DecryptedMailboxListener listener) {
|
||||||
Log.traceCall();
|
|
||||||
decryptedMailboxListeners.remove(listener);
|
decryptedMailboxListeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addP2PServiceListener(P2PServiceListener listener) {
|
public void addP2PServiceListener(P2PServiceListener listener) {
|
||||||
Log.traceCall();
|
|
||||||
p2pServiceListeners.add(listener);
|
p2pServiceListeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeP2PServiceListener(P2PServiceListener listener) {
|
public void removeP2PServiceListener(P2PServiceListener listener) {
|
||||||
Log.traceCall();
|
|
||||||
p2pServiceListeners.remove(listener);
|
p2pServiceListeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addHashSetChangedListener(HashMapChangedListener hashMapChangedListener) {
|
public void addHashSetChangedListener(HashMapChangedListener hashMapChangedListener) {
|
||||||
Log.traceCall();
|
|
||||||
dataStorage.addHashMapChangedListener(hashMapChangedListener);
|
dataStorage.addHashMapChangedListener(hashMapChangedListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -680,49 +690,27 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
|
||||||
return authenticatedPeerAddresses;
|
return authenticatedPeerAddresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ReadOnlyIntegerProperty getNumAuthenticatedPeers() {
|
||||||
|
return numAuthenticatedPeers;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Private
|
// Private
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private HashSet<ProtectedData> getDataSet() {
|
private HashSet<ProtectedData> getDataSet() {
|
||||||
// Log.traceCall();
|
return new HashSet<>(getDataMap().values());
|
||||||
return new HashSet<>(dataStorage.getMap().values());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void tryDecryptMailboxData(ProtectedMailboxData mailboxData) {
|
private boolean verifyAddressPrefixHash(SealedAndSignedMessage sealedAndSignedMessage) {
|
||||||
Log.traceCall();
|
if (myOnionAddress != null) {
|
||||||
if (encryptionService != null) {
|
byte[] blurredAddressHash = myOnionAddress.getAddressPrefixHash();
|
||||||
ExpirablePayload expirablePayload = mailboxData.expirablePayload;
|
return blurredAddressHash != null &&
|
||||||
if (expirablePayload instanceof ExpirableMailboxPayload) {
|
Arrays.equals(blurredAddressHash, sealedAndSignedMessage.addressPrefixHash);
|
||||||
ExpirableMailboxPayload expirableMailboxPayload = (ExpirableMailboxPayload) expirablePayload;
|
|
||||||
SealedAndSignedMessage sealedAndSignedMessage = expirableMailboxPayload.sealedAndSignedMessage;
|
|
||||||
if (verifyBlurredAddressHash(sealedAndSignedMessage)) {
|
|
||||||
try {
|
|
||||||
DecryptedMsgWithPubKey decryptedMsgWithPubKey = encryptionService.decryptAndVerify(
|
|
||||||
sealedAndSignedMessage.sealedAndSigned);
|
|
||||||
if (decryptedMsgWithPubKey.message instanceof MailboxMessage) {
|
|
||||||
MailboxMessage mailboxMessage = (MailboxMessage) decryptedMsgWithPubKey.message;
|
|
||||||
Address senderAddress = mailboxMessage.getSenderAddress();
|
|
||||||
checkNotNull(senderAddress, "senderAddress must not be null for mailbox messages");
|
|
||||||
|
|
||||||
mailboxMap.put(decryptedMsgWithPubKey, mailboxData);
|
|
||||||
log.trace("Decryption of SealedAndSignedMessage succeeded. senderAddress="
|
|
||||||
+ senderAddress + " / my address=" + getAddress());
|
|
||||||
decryptedMailboxListeners.stream().forEach(
|
|
||||||
e -> e.onMailboxMessageAdded(decryptedMsgWithPubKey, senderAddress));
|
|
||||||
} else {
|
} else {
|
||||||
log.warn("tryDecryptMailboxData: Expected MailboxMessage but got other type. " +
|
log.warn("myOnionAddress must not be null at verifyAddressPrefixHash");
|
||||||
"decryptedMsgWithPubKey.message=", decryptedMsgWithPubKey.message);
|
return false;
|
||||||
}
|
|
||||||
} catch (CryptoException e) {
|
|
||||||
log.trace("Decryption of SealedAndSignedMessage failed. " +
|
|
||||||
"That is expected if the message is not intended for us. " + e.getMessage());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.info("Wrong blurredAddressHash. The message is not intended for us.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ public class Connection implements MessageListener {
|
||||||
private static final Logger log = LoggerFactory.getLogger(Connection.class);
|
private static final Logger log = LoggerFactory.getLogger(Connection.class);
|
||||||
private static final int MAX_MSG_SIZE = 5 * 1024 * 1024; // 5 MB of compressed data
|
private static final int MAX_MSG_SIZE = 5 * 1024 * 1024; // 5 MB of compressed data
|
||||||
private static final int SOCKET_TIMEOUT = 30 * 60 * 1000; // 30 min.
|
private static final int SOCKET_TIMEOUT = 30 * 60 * 1000; // 30 min.
|
||||||
|
private ConnectionsType connectionType;
|
||||||
|
|
||||||
public static int getMaxMsgSize() {
|
public static int getMaxMsgSize() {
|
||||||
return MAX_MSG_SIZE;
|
return MAX_MSG_SIZE;
|
||||||
|
@ -122,6 +123,10 @@ public class Connection implements MessageListener {
|
||||||
connectionListener.onPeerAddressAuthenticated(peerAddress, connection);
|
connectionListener.onPeerAddressAuthenticated(peerAddress, connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setConnectionType(ConnectionsType connectionType) {
|
||||||
|
this.connectionType = connectionType;
|
||||||
|
}
|
||||||
|
|
||||||
// Called form various threads
|
// Called form various threads
|
||||||
public void sendMessage(Message message) {
|
public void sendMessage(Message message) {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
|
@ -205,6 +210,9 @@ public class Connection implements MessageListener {
|
||||||
return stopped;
|
return stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ConnectionsType getConnectionType() {
|
||||||
|
return connectionType;
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// ShutDown
|
// ShutDown
|
||||||
|
@ -324,6 +332,7 @@ public class Connection implements MessageListener {
|
||||||
", isAuthenticated=" + isAuthenticated +
|
", isAuthenticated=" + isAuthenticated +
|
||||||
", stopped=" + stopped +
|
", stopped=" + stopped +
|
||||||
", stopped=" + stopped +
|
", stopped=" + stopped +
|
||||||
|
", connectionType=" + connectionType +
|
||||||
", useCompression=" + useCompression +
|
", useCompression=" + useCompression +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package io.bitsquare.p2p.network;
|
||||||
|
|
||||||
|
public enum ConnectionsType {
|
||||||
|
PASSIVE, // for connections initiated by other peer
|
||||||
|
ACTIVE, // for connections initiated by us
|
||||||
|
DIRECT_MSG, // for connections used for direct messaging
|
||||||
|
AUTH_REQUEST // for connections used for starting the authentication
|
||||||
|
}
|
|
@ -27,8 +27,8 @@ import java.util.function.Consumer;
|
||||||
public class LocalhostNetworkNode extends NetworkNode {
|
public class LocalhostNetworkNode extends NetworkNode {
|
||||||
private static final Logger log = LoggerFactory.getLogger(LocalhostNetworkNode.class);
|
private static final Logger log = LoggerFactory.getLogger(LocalhostNetworkNode.class);
|
||||||
|
|
||||||
private static volatile int simulateTorDelayTorNode = 2 * 100;
|
private static volatile int simulateTorDelayTorNode = 600;
|
||||||
private static volatile int simulateTorDelayHiddenService = 2 * 100;
|
private static volatile int simulateTorDelayHiddenService = 3000;
|
||||||
private Address address;
|
private Address address;
|
||||||
|
|
||||||
public static void setSimulateTorDelayTorNode(int simulateTorDelayTorNode) {
|
public static void setSimulateTorDelayTorNode(int simulateTorDelayTorNode) {
|
||||||
|
|
|
@ -87,17 +87,20 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
|
||||||
if (connection != null) {
|
if (connection != null) {
|
||||||
return sendMessage(connection, message);
|
return sendMessage(connection, message);
|
||||||
} else {
|
} else {
|
||||||
log.debug("inBoundConnections " + inBoundConnections.toString());
|
|
||||||
log.debug("outBoundConnections " + outBoundConnections.toString());
|
|
||||||
log.trace("We have not found any connection for that peerAddress. " +
|
log.trace("We have not found any connection for that peerAddress. " +
|
||||||
"We will create a new outbound connection.");
|
"We will create a new outbound connection.");
|
||||||
|
|
||||||
final SettableFuture<Connection> resultFuture = SettableFuture.create();
|
final SettableFuture<Connection> resultFuture = SettableFuture.create();
|
||||||
|
final boolean[] timeoutOccurred = new boolean[1];
|
||||||
|
timeoutOccurred[0] = false;
|
||||||
ListenableFuture<Connection> future = executorService.submit(() -> {
|
ListenableFuture<Connection> future = executorService.submit(() -> {
|
||||||
Thread.currentThread().setName("NetworkNode:SendMessage-to-" + peerAddress);
|
Thread.currentThread().setName("NetworkNode:SendMessage-to-" + peerAddress);
|
||||||
try {
|
try {
|
||||||
Socket socket = createSocket(peerAddress); // can take a while when using tor
|
Socket socket = createSocket(peerAddress); // can take a while when using tor
|
||||||
|
if (timeoutOccurred[0])
|
||||||
|
throw new TimeoutException("Timeout occurred when tried to create Socket to peer: " + peerAddress);
|
||||||
|
|
||||||
|
|
||||||
Connection newConnection = new Connection(socket, NetworkNode.this, NetworkNode.this);
|
Connection newConnection = new Connection(socket, NetworkNode.this, NetworkNode.this);
|
||||||
newConnection.setPeerAddress(peerAddress);
|
newConnection.setPeerAddress(peerAddress);
|
||||||
outBoundConnections.add(newConnection);
|
outBoundConnections.add(newConnection);
|
||||||
|
@ -125,10 +128,11 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Thread.currentThread().setName("TimerTask-" + new Random().nextInt(10000));
|
Thread.currentThread().setName("TimerTask-" + new Random().nextInt(10000));
|
||||||
|
timeoutOccurred[0] = true;
|
||||||
future.cancel(true);
|
future.cancel(true);
|
||||||
String message = "Timeout occurred when tried to create Socket to peer: " + peerAddress;
|
String message = "Timeout occurred when tried to create Socket to peer: " + peerAddress;
|
||||||
log.info(message);
|
log.info(message);
|
||||||
resultFuture.setException(new TimeoutException(message));
|
UserThread.execute(() -> resultFuture.setException(new TimeoutException(message)));
|
||||||
}
|
}
|
||||||
}, CREATE_SOCKET_TIMEOUT);
|
}, CREATE_SOCKET_TIMEOUT);
|
||||||
|
|
||||||
|
@ -335,15 +339,13 @@ public abstract class NetworkNode implements MessageListener, ConnectionListener
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<Connection> lookupOutboundConnection(Address peerAddress) {
|
private Optional<Connection> lookupOutboundConnection(Address peerAddress) {
|
||||||
Log.traceCall(peerAddress.toString());
|
Log.traceCall("search for " + peerAddress.toString() + " / outBoundConnections " + outBoundConnections);
|
||||||
log.debug("outBoundConnections " + outBoundConnections);
|
|
||||||
return outBoundConnections.stream()
|
return outBoundConnections.stream()
|
||||||
.filter(e -> peerAddress.equals(e.getPeerAddress())).findAny();
|
.filter(e -> peerAddress.equals(e.getPeerAddress())).findAny();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<Connection> lookupInboundConnection(Address peerAddress) {
|
private Optional<Connection> lookupInboundConnection(Address peerAddress) {
|
||||||
Log.traceCall(peerAddress.toString());
|
Log.traceCall("search for " + peerAddress.toString() + " / inBoundConnections " + inBoundConnections);
|
||||||
log.debug("inBoundConnections " + inBoundConnections);
|
|
||||||
return inBoundConnections.stream()
|
return inBoundConnections.stream()
|
||||||
.filter(e -> peerAddress.equals(e.getPeerAddress())).findAny();
|
.filter(e -> peerAddress.equals(e.getPeerAddress())).findAny();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,20 +4,23 @@ import com.google.common.util.concurrent.FutureCallback;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.SettableFuture;
|
import com.google.common.util.concurrent.SettableFuture;
|
||||||
import io.bitsquare.app.Log;
|
import io.bitsquare.app.Log;
|
||||||
|
import io.bitsquare.common.UserThread;
|
||||||
import io.bitsquare.p2p.Address;
|
import io.bitsquare.p2p.Address;
|
||||||
import io.bitsquare.p2p.Message;
|
import io.bitsquare.p2p.Message;
|
||||||
import io.bitsquare.p2p.network.Connection;
|
import io.bitsquare.p2p.network.Connection;
|
||||||
|
import io.bitsquare.p2p.network.ConnectionsType;
|
||||||
import io.bitsquare.p2p.network.MessageListener;
|
import io.bitsquare.p2p.network.MessageListener;
|
||||||
import io.bitsquare.p2p.network.NetworkNode;
|
import io.bitsquare.p2p.network.NetworkNode;
|
||||||
import io.bitsquare.p2p.peers.messages.auth.*;
|
import io.bitsquare.p2p.peers.messages.auth.*;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.Set;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
|
|
||||||
// authentication example:
|
// authentication example:
|
||||||
|
@ -51,8 +54,6 @@ public class AuthenticationHandshake implements MessageListener {
|
||||||
this.networkNode = networkNode;
|
this.networkNode = networkNode;
|
||||||
this.peerGroup = peerGroup;
|
this.peerGroup = peerGroup;
|
||||||
this.myAddress = myAddress;
|
this.myAddress = myAddress;
|
||||||
|
|
||||||
networkNode.addMessageListener(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -62,74 +63,85 @@ public class AuthenticationHandshake implements MessageListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(Message message, Connection connection) {
|
public void onMessage(Message message, Connection connection) {
|
||||||
|
checkArgument(!stopped);
|
||||||
|
|
||||||
if (message instanceof AuthenticationMessage) {
|
if (message instanceof AuthenticationMessage) {
|
||||||
Log.traceCall(message.toString());
|
Log.traceCall(message.toString());
|
||||||
if (message instanceof AuthenticationResponse) {
|
if (message instanceof AuthenticationResponse) {
|
||||||
// Requesting peer
|
// Requesting peer
|
||||||
|
|
||||||
|
// We use the active connectionType if we started the authentication request to another peer
|
||||||
|
// That is used for protecting eclipse attacks
|
||||||
|
connection.setConnectionType(ConnectionsType.ACTIVE);
|
||||||
|
|
||||||
AuthenticationResponse authenticationResponse = (AuthenticationResponse) message;
|
AuthenticationResponse authenticationResponse = (AuthenticationResponse) message;
|
||||||
connection.setPeerAddress(authenticationResponse.address);
|
|
||||||
Address peerAddress = authenticationResponse.address;
|
Address peerAddress = authenticationResponse.address;
|
||||||
log.trace("ChallengeMessage from " + peerAddress + " at " + myAddress);
|
connection.setPeerAddress(peerAddress);
|
||||||
|
log.trace("Received authenticationResponse from " + peerAddress);
|
||||||
boolean verified = nonce != 0 && nonce == authenticationResponse.requesterNonce;
|
boolean verified = nonce != 0 && nonce == authenticationResponse.requesterNonce;
|
||||||
if (verified) {
|
if (verified) {
|
||||||
SettableFuture<Connection> future = networkNode.sendMessage(peerAddress,
|
GetPeersAuthRequest getPeersAuthRequest = new GetPeersAuthRequest(myAddress,
|
||||||
new GetPeersAuthRequest(myAddress, authenticationResponse.responderNonce, new HashSet<>(peerGroup.getAllPeerAddresses())));
|
authenticationResponse.responderNonce,
|
||||||
|
new HashSet<>(peerGroup.getAllPeerAddresses()));
|
||||||
|
SettableFuture<Connection> future = networkNode.sendMessage(peerAddress, getPeersAuthRequest);
|
||||||
|
log.trace("Sent GetPeersAuthRequest {} to {}", getPeersAuthRequest, peerAddress);
|
||||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Connection connection) {
|
public void onSuccess(Connection connection) {
|
||||||
log.trace("GetPeersAuthRequest sent successfully from " + myAddress + " to " + peerAddress);
|
log.trace("Successfully sent GetPeersAuthRequest {} to {}", getPeersAuthRequest, peerAddress);
|
||||||
connection.setPeerAddress(peerAddress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NotNull Throwable throwable) {
|
public void onFailure(@NotNull Throwable throwable) {
|
||||||
log.info("GetPeersAuthRequest sending failed " + throwable.getMessage());
|
log.info("GetPeersAuthRequest sending failed " + throwable.getMessage());
|
||||||
onFault(throwable);
|
failed(throwable);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// We could set already the authenticated flag here already, but as we need the reported peers we need
|
||||||
|
// to wait for the GetPeersAuthResponse before we are completed.
|
||||||
} else {
|
} else {
|
||||||
log.warn("verify nonce failed. challengeMessage=" + authenticationResponse + " / nonce=" + nonce);
|
log.warn("verify nonce failed. AuthenticationResponse=" + authenticationResponse + " / nonce=" + nonce);
|
||||||
onFault(new Exception("Verify nonce failed. challengeMessage=" + authenticationResponse + " / nonceMap=" + nonce));
|
failed(new Exception("Verify nonce failed. AuthenticationResponse=" + authenticationResponse + " / nonceMap=" + nonce));
|
||||||
}
|
}
|
||||||
} else if (message instanceof GetPeersAuthRequest) {
|
} else if (message instanceof GetPeersAuthRequest) {
|
||||||
// Responding peer
|
// Responding peer
|
||||||
GetPeersAuthRequest getPeersAuthRequest = (GetPeersAuthRequest) message;
|
GetPeersAuthRequest getPeersAuthRequest = (GetPeersAuthRequest) message;
|
||||||
Address peerAddress = getPeersAuthRequest.address;
|
Address peerAddress = getPeersAuthRequest.address;
|
||||||
log.trace("GetPeersMessage from " + peerAddress + " at " + myAddress);
|
log.trace("GetPeersAuthRequest from " + peerAddress + " at " + myAddress);
|
||||||
boolean verified = nonce != 0 && nonce == getPeersAuthRequest.responderNonce;
|
boolean verified = nonce != 0 && nonce == getPeersAuthRequest.responderNonce;
|
||||||
if (verified) {
|
if (verified) {
|
||||||
// we create the msg with our already collected peer addresses (before adding the new ones)
|
// we create the msg with our already collected peer addresses (before adding the new ones)
|
||||||
SettableFuture<Connection> future = networkNode.sendMessage(peerAddress,
|
GetPeersAuthResponse getPeersAuthResponse = new GetPeersAuthResponse(myAddress,
|
||||||
new GetPeersAuthResponse(myAddress, new HashSet<>(peerGroup.getAllPeerAddresses())));
|
new HashSet<>(peerGroup.getAllPeerAddresses()));
|
||||||
log.trace("sent GetPeersAuthResponse to " + peerAddress + " from " + myAddress
|
SettableFuture<Connection> future = networkNode.sendMessage(peerAddress, getPeersAuthResponse);
|
||||||
+ " with allPeers=" + peerGroup.getAllPeerAddresses());
|
log.trace("Sent GetPeersAuthResponse {} to {}", getPeersAuthResponse, peerAddress);
|
||||||
|
|
||||||
// now we add the reported peers to our own set
|
// now we add the reported peers to our own set
|
||||||
HashSet<Address> peerAddresses = getPeersAuthRequest.peerAddresses;
|
HashSet<Address> peerAddresses = getPeersAuthRequest.peerAddresses;
|
||||||
log.trace("Received peers: " + peerAddresses);
|
log.trace("Received reported peers: " + peerAddresses);
|
||||||
peerGroup.addToReportedPeers(peerAddresses, connection);
|
peerGroup.addToReportedPeers(peerAddresses, connection);
|
||||||
|
|
||||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Connection connection) {
|
public void onSuccess(Connection connection) {
|
||||||
log.trace("GetPeersAuthResponse sent successfully from " + myAddress + " to " + peerAddress);
|
log.trace("Successfully sent GetPeersAuthResponse {} to {}", getPeersAuthResponse, peerAddress);
|
||||||
connection.setPeerAddress(peerAddress);
|
|
||||||
log.info("AuthenticationComplete: Peer with address " + peerAddress
|
log.info("AuthenticationComplete: Peer with address " + peerAddress
|
||||||
+ " authenticated (" + connection.getUid() + "). Took "
|
+ " authenticated (" + connection.getUid() + "). Took "
|
||||||
+ (System.currentTimeMillis() - startAuthTs) + " ms.");
|
+ (System.currentTimeMillis() - startAuthTs) + " ms.");
|
||||||
|
|
||||||
AuthenticationHandshake.this.onSuccess(connection);
|
completed(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NotNull Throwable throwable) {
|
public void onFailure(@NotNull Throwable throwable) {
|
||||||
log.info("GetPeersAuthResponse sending failed " + throwable.getMessage());
|
log.info("GetPeersAuthResponse sending failed " + throwable.getMessage());
|
||||||
onFault(throwable);
|
failed(throwable);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
log.warn("verify nonce failed. getPeersMessage=" + getPeersAuthRequest + " / nonce=" + nonce);
|
log.warn("verify nonce failed. getPeersMessage=" + getPeersAuthRequest + " / nonce=" + nonce);
|
||||||
onFault(new Exception("Verify nonce failed. getPeersMessage=" + getPeersAuthRequest + " / nonce=" + nonce));
|
failed(new Exception("Verify nonce failed. getPeersMessage=" + getPeersAuthRequest + " / nonce=" + nonce));
|
||||||
}
|
}
|
||||||
} else if (message instanceof GetPeersAuthResponse) {
|
} else if (message instanceof GetPeersAuthResponse) {
|
||||||
// Requesting peer
|
// Requesting peer
|
||||||
|
@ -137,130 +149,97 @@ public class AuthenticationHandshake implements MessageListener {
|
||||||
Address peerAddress = getPeersAuthResponse.address;
|
Address peerAddress = getPeersAuthResponse.address;
|
||||||
log.trace("GetPeersAuthResponse from " + peerAddress + " at " + myAddress);
|
log.trace("GetPeersAuthResponse from " + peerAddress + " at " + myAddress);
|
||||||
HashSet<Address> peerAddresses = getPeersAuthResponse.peerAddresses;
|
HashSet<Address> peerAddresses = getPeersAuthResponse.peerAddresses;
|
||||||
log.trace("Received peers: " + peerAddresses);
|
log.trace("Received reported peers: " + peerAddresses);
|
||||||
peerGroup.addToReportedPeers(peerAddresses, connection);
|
peerGroup.addToReportedPeers(peerAddresses, connection);
|
||||||
|
|
||||||
// we wait until the handshake is completed before setting the authenticate flag
|
|
||||||
// authentication at both sides of the connection
|
|
||||||
log.info("AuthenticationComplete: Peer with address " + peerAddress
|
log.info("AuthenticationComplete: Peer with address " + peerAddress
|
||||||
+ " authenticated (" + connection.getUid() + "). Took "
|
+ " authenticated (" + connection.getUid() + "). Took "
|
||||||
+ (System.currentTimeMillis() - startAuthTs) + " ms.");
|
+ (System.currentTimeMillis() - startAuthTs) + " ms.");
|
||||||
|
|
||||||
onSuccess(connection);
|
completed(connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// API
|
// Authentication initiated by requesting peer
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public SettableFuture<Connection> requestAuthenticationToPeer(Address peerAddress) {
|
public SettableFuture<Connection> requestAuthentication(Address peerAddress) {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
// Requesting peer
|
// Requesting peer
|
||||||
resultFuture = SettableFuture.create();
|
|
||||||
startAuthTs = System.currentTimeMillis();
|
|
||||||
SettableFuture<Connection> future = networkNode.sendMessage(peerAddress, new AuthenticationRequest(myAddress, getAndSetNonce()));
|
|
||||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(@Nullable Connection connection) {
|
|
||||||
log.trace("send RequestAuthenticationMessage to " + peerAddress + " succeeded.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
init();
|
||||||
public void onFailure(@NotNull Throwable throwable) {
|
|
||||||
log.info("Send RequestAuthenticationMessage to " + peerAddress + " failed." +
|
|
||||||
"\nException:" + throwable.getMessage());
|
|
||||||
onFault(throwable);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return resultFuture;
|
AuthenticationRequest authenticationRequest = new AuthenticationRequest(myAddress, getAndSetNonce());
|
||||||
}
|
SettableFuture<Connection> future = networkNode.sendMessage(peerAddress, authenticationRequest);
|
||||||
|
|
||||||
public SettableFuture<Connection> requestAuthentication(Set<Address> remainingAddresses, Address peerAddress) {
|
|
||||||
Log.traceCall(peerAddress.getFullAddress());
|
|
||||||
// Requesting peer
|
|
||||||
resultFuture = SettableFuture.create();
|
|
||||||
startAuthTs = System.currentTimeMillis();
|
|
||||||
remainingAddresses.remove(peerAddress);
|
|
||||||
SettableFuture<Connection> future = networkNode.sendMessage(peerAddress, new AuthenticationRequest(myAddress, getAndSetNonce()));
|
|
||||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(@Nullable Connection connection) {
|
|
||||||
log.trace("send RequestAuthenticationMessage to " + peerAddress + " succeeded.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(@NotNull Throwable throwable) {
|
|
||||||
log.info("Send RequestAuthenticationMessage to " + peerAddress + " failed." +
|
|
||||||
"\nThat is expected if seed nodes are offline." +
|
|
||||||
"\nException:" + throwable.getMessage());
|
|
||||||
onFault(throwable);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return resultFuture;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SettableFuture<Connection> processAuthenticationRequest(AuthenticationRequest authenticationRequest, Connection connection) {
|
|
||||||
Log.traceCall();
|
|
||||||
// Responding peer
|
|
||||||
resultFuture = SettableFuture.create();
|
|
||||||
startAuthTs = System.currentTimeMillis();
|
|
||||||
|
|
||||||
Address peerAddress = authenticationRequest.address;
|
|
||||||
log.trace("RequestAuthenticationMessage from " + peerAddress + " at " + myAddress);
|
|
||||||
log.info("We shut down inbound connection from peer {} to establish a new " +
|
|
||||||
"connection with his reported address.", peerAddress);
|
|
||||||
|
|
||||||
//TODO check if causes problems without delay
|
|
||||||
connection.shutDown(() -> {
|
|
||||||
Log.traceCall();
|
|
||||||
if (!stopped) {
|
|
||||||
// we delay a bit as listeners for connection.onDisconnect are on other threads and might lead to
|
|
||||||
// inconsistent state (removal of connection from NetworkNode.authenticatedConnections)
|
|
||||||
log.trace("processAuthenticationMessage: connection.shutDown complete. RequestAuthenticationMessage from " + peerAddress + " at " + myAddress);
|
|
||||||
|
|
||||||
SettableFuture<Connection> future = networkNode.sendMessage(peerAddress, new AuthenticationResponse(myAddress, authenticationRequest.requesterNonce, getAndSetNonce()));
|
|
||||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Connection connection) {
|
public void onSuccess(Connection connection) {
|
||||||
log.trace("onSuccess sending ChallengeMessage");
|
log.trace("send AuthenticationRequest to " + peerAddress + " succeeded.");
|
||||||
|
connection.setPeerAddress(peerAddress);
|
||||||
|
// We protect that connection from getting closed by maintenance cleanup...
|
||||||
|
connection.setConnectionType(ConnectionsType.AUTH_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NotNull Throwable throwable) {
|
public void onFailure(@NotNull Throwable throwable) {
|
||||||
log.warn("onFailure sending ChallengeMessage.");
|
log.info("Send AuthenticationRequest to " + peerAddress + " failed." +
|
||||||
onFault(throwable);
|
"\nException:" + throwable.getMessage());
|
||||||
|
failed(throwable);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return resultFuture;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
/* connection.shutDown(() -> UserThread.runAfter(() -> { Log.traceCall();
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Responding to authentication request
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public SettableFuture<Connection> respondToAuthenticationRequest(AuthenticationRequest authenticationRequest, Connection connection) {
|
||||||
|
Log.traceCall();
|
||||||
|
// Responding peer
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
|
Address peerAddress = authenticationRequest.address;
|
||||||
|
log.trace("AuthenticationRequest from " + peerAddress + " at " + myAddress);
|
||||||
|
log.info("We shut down inbound connection from peer {} to establish a new " +
|
||||||
|
"connection with his reported address.", peerAddress);
|
||||||
|
|
||||||
|
connection.shutDown(() -> {
|
||||||
|
UserThread.runAfter(() -> {
|
||||||
if (!stopped) {
|
if (!stopped) {
|
||||||
// we delay a bit as listeners for connection.onDisconnect are on other threads and might lead to
|
// we delay a bit as listeners for connection.onDisconnect are on other threads and might lead to
|
||||||
// inconsistent state (removal of connection from NetworkNode.authenticatedConnections)
|
// inconsistent state (removal of connection from NetworkNode.authenticatedConnections)
|
||||||
log.trace("processAuthenticationMessage: connection.shutDown complete. RequestAuthenticationMessage from " + peerAddress + " at " + myAddress);
|
log.trace("processAuthenticationMessage: connection.shutDown complete. AuthenticationRequest from " + peerAddress + " at " + myAddress);
|
||||||
|
|
||||||
SettableFuture<Connection> future = networkNode.sendMessage(peerAddress, new AuthenticationResponse(myAddress, authenticationRequest.nonce, getAndSetNonce()));
|
AuthenticationResponse authenticationResponse = new AuthenticationResponse(myAddress,
|
||||||
|
authenticationRequest.requesterNonce,
|
||||||
|
getAndSetNonce());
|
||||||
|
SettableFuture<Connection> future = networkNode.sendMessage(peerAddress, authenticationResponse);
|
||||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Connection connection) { Log.traceCall();
|
public void onSuccess(Connection connection) {
|
||||||
log.trace("onSuccess sending ChallengeMessage");
|
log.trace("onSuccess sending AuthenticationResponse");
|
||||||
|
connection.setPeerAddress(peerAddress);
|
||||||
|
// We use passive connectionType for connections created from received authentication requests from other peers
|
||||||
|
// That is used for protecting eclipse attacks
|
||||||
|
connection.setConnectionType(ConnectionsType.PASSIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NotNull Throwable throwable) { Log.traceCall();
|
public void onFailure(@NotNull Throwable throwable) {
|
||||||
log.warn("onFailure sending ChallengeMessage.");
|
log.warn("onFailure sending AuthenticationResponse.");
|
||||||
onFault(throwable);
|
failed(throwable);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}, 200, TimeUnit.MILLISECONDS);
|
||||||
100 + PeerGroup.simulateAuthTorNode,
|
});
|
||||||
TimeUnit.MILLISECONDS));*/
|
|
||||||
|
|
||||||
return resultFuture;
|
return resultFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,6 +248,12 @@ public class AuthenticationHandshake implements MessageListener {
|
||||||
// Private
|
// Private
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
networkNode.addMessageListener(this);
|
||||||
|
resultFuture = SettableFuture.create();
|
||||||
|
startAuthTs = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
private long getAndSetNonce() {
|
private long getAndSetNonce() {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
nonce = new Random().nextLong();
|
nonce = new Random().nextLong();
|
||||||
|
@ -278,21 +263,21 @@ public class AuthenticationHandshake implements MessageListener {
|
||||||
return nonce;
|
return nonce;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onFault(@NotNull Throwable throwable) {
|
private void failed(@NotNull Throwable throwable) {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
cleanup();
|
shutDown();
|
||||||
resultFuture.setException(throwable);
|
resultFuture.setException(throwable);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onSuccess(Connection connection) {
|
private void completed(Connection connection) {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
cleanup();
|
shutDown();
|
||||||
resultFuture.set(connection);
|
resultFuture.set(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cleanup() {
|
private void shutDown() {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
stopped = true;
|
|
||||||
networkNode.removeMessageListener(this);
|
networkNode.removeMessageListener(this);
|
||||||
|
stopped = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
package io.bitsquare.p2p.peers;
|
|
||||||
|
|
||||||
import io.bitsquare.p2p.Address;
|
|
||||||
import io.bitsquare.p2p.network.Connection;
|
|
||||||
|
|
||||||
//TODO used only in unittests yet
|
|
||||||
public abstract class AuthenticationListener implements PeerListener {
|
|
||||||
public void onFirstAuthenticatePeer(Peer peer) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onPeerAdded(Peer peer) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onPeerRemoved(Address address) {
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract public void onConnectionAuthenticated(Connection connection);
|
|
||||||
}
|
|
|
@ -8,10 +8,7 @@ import io.bitsquare.common.UserThread;
|
||||||
import io.bitsquare.common.util.Tuple2;
|
import io.bitsquare.common.util.Tuple2;
|
||||||
import io.bitsquare.p2p.Address;
|
import io.bitsquare.p2p.Address;
|
||||||
import io.bitsquare.p2p.Message;
|
import io.bitsquare.p2p.Message;
|
||||||
import io.bitsquare.p2p.network.Connection;
|
import io.bitsquare.p2p.network.*;
|
||||||
import io.bitsquare.p2p.network.ConnectionListener;
|
|
||||||
import io.bitsquare.p2p.network.MessageListener;
|
|
||||||
import io.bitsquare.p2p.network.NetworkNode;
|
|
||||||
import io.bitsquare.p2p.peers.messages.auth.AuthenticationRequest;
|
import io.bitsquare.p2p.peers.messages.auth.AuthenticationRequest;
|
||||||
import io.bitsquare.p2p.peers.messages.maintenance.*;
|
import io.bitsquare.p2p.peers.messages.maintenance.*;
|
||||||
import io.bitsquare.p2p.storage.messages.DataBroadcastMessage;
|
import io.bitsquare.p2p.storage.messages.DataBroadcastMessage;
|
||||||
|
@ -21,7 +18,6 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@ -37,10 +33,12 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
PeerGroup.simulateAuthTorNode = simulateAuthTorNode;
|
PeerGroup.simulateAuthTorNode = simulateAuthTorNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int MAX_CONNECTIONS = 8;
|
private static int MAX_CONNECTIONS_LOW_PRIO = 8;
|
||||||
|
private static int MAX_CONNECTIONS_NORMAL_PRIO = MAX_CONNECTIONS_LOW_PRIO + 4;
|
||||||
|
private static int MAX_CONNECTIONS_HIGH_PRIO = MAX_CONNECTIONS_NORMAL_PRIO + 4;
|
||||||
|
|
||||||
public static void setMaxConnections(int maxConnections) {
|
public static void setMaxConnectionsLowPrio(int maxConnectionsLowPrio) {
|
||||||
MAX_CONNECTIONS = maxConnections;
|
MAX_CONNECTIONS_LOW_PRIO = maxConnectionsLowPrio;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int PING_AFTER_CONNECTION_INACTIVITY = 30 * 1000;
|
private static final int PING_AFTER_CONNECTION_INACTIVITY = 30 * 1000;
|
||||||
|
@ -49,16 +47,14 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
private final NetworkNode networkNode;
|
private final NetworkNode networkNode;
|
||||||
private final Set<Address> seedNodeAddresses;
|
private final Set<Address> seedNodeAddresses;
|
||||||
|
|
||||||
private final Set<PeerListener> peerListeners = new HashSet<>();
|
|
||||||
private final Map<Address, Peer> authenticatedPeers = new HashMap<>();
|
private final Map<Address, Peer> authenticatedPeers = new HashMap<>();
|
||||||
private final Set<Address> reportedPeerAddresses = new HashSet<>();
|
private final Set<Address> reportedPeerAddresses = new HashSet<>();
|
||||||
private final Map<Address, AuthenticationHandshake> authenticationHandshakes = new ConcurrentHashMap<>();
|
private final Map<Address, AuthenticationHandshake> authenticationHandshakes = new HashMap<>();
|
||||||
|
|
||||||
private Timer sendPingTimer = new Timer();
|
private Timer sendPingTimer = new Timer();
|
||||||
private Timer getPeersTimer = new Timer();
|
private Timer getPeersTimer = new Timer();
|
||||||
|
|
||||||
private boolean shutDownInProgress;
|
private boolean shutDownInProgress;
|
||||||
private boolean firstPeerAdded = false;
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -164,16 +160,18 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Authentication to seed node
|
// Process incoming authentication request
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private void processAuthenticationRequest(NetworkNode networkNode, AuthenticationRequest message, final Connection connection) {
|
private void processAuthenticationRequest(NetworkNode networkNode, AuthenticationRequest message, final Connection connection) {
|
||||||
Log.traceCall(message.toString());
|
Log.traceCall(message.toString());
|
||||||
Address peerAddress = message.address;
|
Address peerAddress = message.address;
|
||||||
if (!authenticationHandshakes.containsKey(peerAddress)) {
|
if (!authenticationHandshakes.containsKey(peerAddress)) {
|
||||||
|
// We protect that connection from getting closed by maintenance cleanup...
|
||||||
|
connection.setConnectionType(ConnectionsType.AUTH_REQUEST);
|
||||||
AuthenticationHandshake authenticationHandshake = new AuthenticationHandshake(networkNode, PeerGroup.this, getMyAddress());
|
AuthenticationHandshake authenticationHandshake = new AuthenticationHandshake(networkNode, PeerGroup.this, getMyAddress());
|
||||||
authenticationHandshakes.put(peerAddress, authenticationHandshake);
|
authenticationHandshakes.put(peerAddress, authenticationHandshake);
|
||||||
SettableFuture<Connection> future = authenticationHandshake.processAuthenticationRequest(message, connection);
|
SettableFuture<Connection> future = authenticationHandshake.respondToAuthenticationRequest(message, connection);
|
||||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(@Nullable Connection connection) {
|
public void onSuccess(@Nullable Connection connection) {
|
||||||
|
@ -198,29 +196,38 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Authentication to seed node
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// After HS is published or after a retry from a successful GetDataRequest if no seed nodes have been available initially
|
||||||
public void authenticateSeedNode(Address peerAddress) {
|
public void authenticateSeedNode(Address peerAddress) {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
authenticateToSeedNode(new HashSet<>(seedNodeAddresses), peerAddress, true);
|
authenticateToSeedNode(new HashSet<>(seedNodeAddresses), peerAddress, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// First we try to connect to 1 seed node. If we fail we try to connect to any reported peer.
|
// First we try to connect to 1 seed node.
|
||||||
|
// If we fail we try to connect to one of the remaining seed nodes.
|
||||||
|
// If that fails as well we use the reported peers if available.
|
||||||
|
// If there are also no reported peers we retry after a random pause of a few minutes.
|
||||||
|
//
|
||||||
// After connection is authenticated, we try to connect to any reported peer as long we have not
|
// After connection is authenticated, we try to connect to any reported peer as long we have not
|
||||||
// reached our max connection size.
|
// reached our max connection size.
|
||||||
private void authenticateToSeedNode(Set<Address> remainingAddresses, Address peerAddress, boolean continueOnSuccess) {
|
private void authenticateToSeedNode(Set<Address> remainingAddresses, Address peerAddress, boolean connectToReportedAfterSuccess) {
|
||||||
Log.traceCall(peerAddress.getFullAddress());
|
Log.traceCall(peerAddress.getFullAddress());
|
||||||
checkArgument(!authenticatedPeers.containsKey(peerAddress),
|
checkArgument(!authenticatedPeers.containsKey(peerAddress),
|
||||||
"We have that peer already authenticated. That must never happen.");
|
"We have that peer already authenticated. That must never happen.");
|
||||||
if (!authenticationHandshakes.containsKey(peerAddress)) {
|
if (!authenticationHandshakes.containsKey(peerAddress)) {
|
||||||
AuthenticationHandshake authenticationHandshake = new AuthenticationHandshake(networkNode, this, getMyAddress());
|
AuthenticationHandshake authenticationHandshake = new AuthenticationHandshake(networkNode, this, getMyAddress());
|
||||||
authenticationHandshakes.put(peerAddress, authenticationHandshake);
|
authenticationHandshakes.put(peerAddress, authenticationHandshake);
|
||||||
SettableFuture<Connection> future = authenticationHandshake.requestAuthentication(remainingAddresses, peerAddress);
|
SettableFuture<Connection> future = authenticationHandshake.requestAuthentication(peerAddress);
|
||||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(@Nullable Connection connection) {
|
public void onSuccess(Connection connection) {
|
||||||
if (connection != null) {
|
|
||||||
setAuthenticated(connection, peerAddress);
|
setAuthenticated(connection, peerAddress);
|
||||||
if (continueOnSuccess) {
|
if (connectToReportedAfterSuccess) {
|
||||||
if (getAuthenticatedPeers().size() <= MAX_CONNECTIONS) {
|
if (getAuthenticatedPeers().size() < MAX_CONNECTIONS_LOW_PRIO) {
|
||||||
log.info("We still don't have enough connections. Lets try the reported peers.");
|
log.info("We still don't have enough connections. Lets try the reported peers.");
|
||||||
authenticateToRemainingReportedPeers(true);
|
authenticateToRemainingReportedPeers(true);
|
||||||
} else {
|
} else {
|
||||||
|
@ -233,7 +240,6 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
1, 2, TimeUnit.MINUTES);
|
1, 2, TimeUnit.MINUTES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NotNull Throwable throwable) {
|
public void onFailure(@NotNull Throwable throwable) {
|
||||||
|
@ -242,12 +248,12 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
"\nException:" + throwable.getMessage());
|
"\nException:" + throwable.getMessage());
|
||||||
removePeer(peerAddress);
|
removePeer(peerAddress);
|
||||||
|
|
||||||
// If we fail we try again with the remaining set
|
// If we fail we try again with the remaining set excluding the failed one
|
||||||
remainingAddresses.remove(peerAddress);
|
remainingAddresses.remove(peerAddress);
|
||||||
|
|
||||||
log.trace("We try to authenticate to another random seed nodes of that list: " + remainingAddresses);
|
log.trace("We try to authenticate to another random seed nodes of that list: " + remainingAddresses);
|
||||||
|
|
||||||
Optional<Tuple2<Address, Set<Address>>> tupleOptional = getRandomItemAndRemainingSet(remainingAddresses);
|
Optional<Tuple2<Address, Set<Address>>> tupleOptional = getRandomNotAuthPeerAndRemainingSet(remainingAddresses);
|
||||||
if (tupleOptional.isPresent()) {
|
if (tupleOptional.isPresent()) {
|
||||||
log.info("We try to authenticate to a seed node. " + tupleOptional.get().first);
|
log.info("We try to authenticate to a seed node. " + tupleOptional.get().first);
|
||||||
authenticateToSeedNode(tupleOptional.get().second, tupleOptional.get().first, true);
|
authenticateToSeedNode(tupleOptional.get().second, tupleOptional.get().first, true);
|
||||||
|
@ -255,8 +261,8 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
log.info("We don't have any more seed nodes for connecting. Lets try the reported peers.");
|
log.info("We don't have any more seed nodes for connecting. Lets try the reported peers.");
|
||||||
authenticateToRemainingReportedPeers(true);
|
authenticateToRemainingReportedPeers(true);
|
||||||
} else {
|
} else {
|
||||||
log.info("We don't have any more seed nodes or reported nodes for connecting. " +
|
log.info("We don't have any more seed nodes nor reported nodes for connecting. " +
|
||||||
"We stop bootstrapping now, but will repeat after an while.");
|
"We stop authentication attempts now, but will repeat after a few minutes.");
|
||||||
UserThread.runAfterRandomDelay(() -> authenticateToRemainingReportedPeers(true),
|
UserThread.runAfterRandomDelay(() -> authenticateToRemainingReportedPeers(true),
|
||||||
1, 2, TimeUnit.MINUTES);
|
1, 2, TimeUnit.MINUTES);
|
||||||
}
|
}
|
||||||
|
@ -267,13 +273,32 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void authenticateToRemainingReportedPeers(boolean calledFromSeedNodeMethod) {
|
private void authenticateToRemainingSeedNodes() {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
Optional<Tuple2<Address, Set<Address>>> tupleOptional = getRandomItemAndRemainingSet(reportedPeerAddresses);
|
Optional<Tuple2<Address, Set<Address>>> tupleOptional = getRandomNotAuthPeerAndRemainingSet(seedNodeAddresses);
|
||||||
|
if (tupleOptional.isPresent()) {
|
||||||
|
log.info("We try to authenticate to a random seed node. " + tupleOptional.get().first);
|
||||||
|
authenticateToSeedNode(tupleOptional.get().second, tupleOptional.get().first, true);
|
||||||
|
} else {
|
||||||
|
log.info("We don't have any more seed nodes for connecting. " +
|
||||||
|
"We stop authentication attempts now, but will repeat after an while.");
|
||||||
|
UserThread.runAfterRandomDelay(() -> authenticateToRemainingReportedPeers(false),
|
||||||
|
1, 2, TimeUnit.MINUTES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Authentication to reported peers
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private void authenticateToRemainingReportedPeers(boolean calledFromAuthenticateToSeedNode) {
|
||||||
|
Log.traceCall();
|
||||||
|
Optional<Tuple2<Address, Set<Address>>> tupleOptional = getRandomNotAuthPeerAndRemainingSet(reportedPeerAddresses);
|
||||||
if (tupleOptional.isPresent()) {
|
if (tupleOptional.isPresent()) {
|
||||||
log.info("We try to authenticate to a random peer. " + tupleOptional.get().first);
|
log.info("We try to authenticate to a random peer. " + tupleOptional.get().first);
|
||||||
authenticateToReportedPeer(tupleOptional.get().second, tupleOptional.get().first);
|
authenticateToReportedPeer(tupleOptional.get().first);
|
||||||
} else if (calledFromSeedNodeMethod) {
|
} else if (calledFromAuthenticateToSeedNode) {
|
||||||
log.info("We don't have any reported peers for connecting. " +
|
log.info("We don't have any reported peers for connecting. " +
|
||||||
"As we tried recently the seed nodes we will wait a bit before repeating.");
|
"As we tried recently the seed nodes we will wait a bit before repeating.");
|
||||||
UserThread.runAfterRandomDelay(() -> authenticateToRemainingSeedNodes(),
|
UserThread.runAfterRandomDelay(() -> authenticateToRemainingSeedNodes(),
|
||||||
|
@ -285,27 +310,26 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
// We try to connect to a reported peer. If we fail we repeat after the failed peer has been removed.
|
// We try to connect to a reported peer. If we fail we repeat after the failed peer has been removed.
|
||||||
// If we succeed we repeat until we are ut of addresses.
|
// If we succeed we repeat until we are out of addresses.
|
||||||
private void authenticateToReportedPeer(Set<Address> remainingAddresses, Address peerAddress) {
|
private void authenticateToReportedPeer(Address peerAddress) {
|
||||||
Log.traceCall(peerAddress.getFullAddress());
|
Log.traceCall(peerAddress.getFullAddress());
|
||||||
checkArgument(!authenticatedPeers.containsKey(peerAddress),
|
checkArgument(!authenticatedPeers.containsKey(peerAddress),
|
||||||
"We have that peer already authenticated. That must never happen.");
|
"We have that peer already authenticated. That must never happen.");
|
||||||
if (!authenticationHandshakes.containsKey(peerAddress)) {
|
if (!authenticationHandshakes.containsKey(peerAddress)) {
|
||||||
AuthenticationHandshake authenticationHandshake = new AuthenticationHandshake(networkNode, this, getMyAddress());
|
AuthenticationHandshake authenticationHandshake = new AuthenticationHandshake(networkNode, this, getMyAddress());
|
||||||
authenticationHandshakes.put(peerAddress, authenticationHandshake);
|
authenticationHandshakes.put(peerAddress, authenticationHandshake);
|
||||||
SettableFuture<Connection> future = authenticationHandshake.requestAuthentication(remainingAddresses, peerAddress);
|
SettableFuture<Connection> future = authenticationHandshake.requestAuthentication(peerAddress);
|
||||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(@Nullable Connection connection) {
|
public void onSuccess(Connection connection) {
|
||||||
if (connection != null) {
|
|
||||||
setAuthenticated(connection, peerAddress);
|
setAuthenticated(connection, peerAddress);
|
||||||
if (getAuthenticatedPeers().size() <= MAX_CONNECTIONS) {
|
if (getAuthenticatedPeers().size() < MAX_CONNECTIONS_LOW_PRIO) {
|
||||||
if (reportedPeerAddresses.size() > 0) {
|
if (reportedPeerAddresses.size() > 0) {
|
||||||
log.info("We still don't have enough connections. " +
|
log.info("We still don't have enough connections. " +
|
||||||
"Lets try the remaining reported peer addresses.");
|
"Lets try the remaining reported peer addresses.");
|
||||||
authenticateToRemainingReportedPeers(false);
|
authenticateToRemainingReportedPeers(false);
|
||||||
} else {
|
} else {
|
||||||
log.info("We still don't have enough connections. " +
|
log.info("We don't have more reported peers and still don't have enough connections. " +
|
||||||
"Lets wait a bit and then try the remaining seed nodes.");
|
"Lets wait a bit and then try the remaining seed nodes.");
|
||||||
UserThread.runAfterRandomDelay(() -> authenticateToRemainingSeedNodes(),
|
UserThread.runAfterRandomDelay(() -> authenticateToRemainingSeedNodes(),
|
||||||
1, 2, TimeUnit.MINUTES);
|
1, 2, TimeUnit.MINUTES);
|
||||||
|
@ -314,16 +338,14 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
log.info("We have already enough connections.");
|
log.info("We have already enough connections.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NotNull Throwable throwable) {
|
public void onFailure(@NotNull Throwable throwable) {
|
||||||
log.error("AuthenticationHandshake failed. " + throwable.getMessage());
|
log.info("Send RequestAuthenticationMessage to a reported peer with address " + peerAddress + " failed." +
|
||||||
throwable.printStackTrace();
|
"\nThat is expected if the nodes was offline." +
|
||||||
|
"\nException:" + throwable.getMessage());
|
||||||
removePeer(peerAddress);
|
removePeer(peerAddress);
|
||||||
|
|
||||||
authenticateToRemainingReportedPeers(false);
|
|
||||||
|
|
||||||
if (reportedPeerAddresses.size() > 0) {
|
if (reportedPeerAddresses.size() > 0) {
|
||||||
log.info("Authentication failed. Lets try again with the remaining reported peer addresses.");
|
log.info("Authentication failed. Lets try again with the remaining reported peer addresses.");
|
||||||
authenticateToRemainingReportedPeers(false);
|
authenticateToRemainingReportedPeers(false);
|
||||||
|
@ -340,40 +362,29 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void authenticateToRemainingSeedNodes() {
|
|
||||||
Log.traceCall();
|
|
||||||
Optional<Tuple2<Address, Set<Address>>> tupleOptional = getRandomItemAndRemainingSet(seedNodeAddresses);
|
|
||||||
if (tupleOptional.isPresent()) {
|
|
||||||
log.info("We try to authenticate to a random seed node. " + tupleOptional.get().first);
|
|
||||||
authenticateToSeedNode(tupleOptional.get().second, tupleOptional.get().first, false);
|
|
||||||
} else {
|
|
||||||
log.info("We don't have any more seed nodes for connecting. " +
|
|
||||||
"We stop bootstrapping now, but will repeat after an while.");
|
|
||||||
UserThread.runAfterRandomDelay(() -> authenticateToRemainingReportedPeers(true),
|
|
||||||
1, 2, TimeUnit.MINUTES);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Authentication to non-seed node peer
|
// Authentication to peer used for direct messaging
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void authenticateToPeer(Address peerAddress, @Nullable Runnable authenticationCompleteHandler, @Nullable Runnable faultHandler) {
|
// Priority is set when we receive a decrypted mail message as those are used for direct messages
|
||||||
|
public void authenticateToDirectMessagePeer(Address peerAddress,
|
||||||
|
@Nullable Runnable completeHandler,
|
||||||
|
@Nullable Runnable faultHandler) {
|
||||||
Log.traceCall(peerAddress.getFullAddress());
|
Log.traceCall(peerAddress.getFullAddress());
|
||||||
checkArgument(!authenticatedPeers.containsKey(peerAddress),
|
checkArgument(!authenticatedPeers.containsKey(peerAddress),
|
||||||
"We have that seed node already authenticated. That must never happen.");
|
"We have that seed node already authenticated. That must never happen.");
|
||||||
if (!authenticationHandshakes.containsKey(peerAddress)) {
|
if (!authenticationHandshakes.containsKey(peerAddress)) {
|
||||||
AuthenticationHandshake authenticationHandshake = new AuthenticationHandshake(networkNode, this, getMyAddress());
|
AuthenticationHandshake authenticationHandshake = new AuthenticationHandshake(networkNode, this, getMyAddress());
|
||||||
authenticationHandshakes.put(peerAddress, authenticationHandshake);
|
authenticationHandshakes.put(peerAddress, authenticationHandshake);
|
||||||
SettableFuture<Connection> future = authenticationHandshake.requestAuthenticationToPeer(peerAddress);
|
SettableFuture<Connection> future = authenticationHandshake.requestAuthentication(peerAddress);
|
||||||
Futures.addCallback(future, new FutureCallback<Connection>() {
|
Futures.addCallback(future, new FutureCallback<Connection>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(@Nullable Connection connection) {
|
public void onSuccess(@Nullable Connection connection) {
|
||||||
if (connection != null) {
|
if (connection != null) {
|
||||||
setAuthenticated(connection, peerAddress);
|
setAuthenticated(connection, peerAddress);
|
||||||
if (authenticationCompleteHandler != null)
|
if (completeHandler != null)
|
||||||
authenticationCompleteHandler.run();
|
completeHandler.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -405,20 +416,13 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
connection.setAuthenticated(peerAddress, connection);
|
connection.setAuthenticated(peerAddress, connection);
|
||||||
|
|
||||||
addAuthenticatedPeer(new Peer(connection));
|
addAuthenticatedPeer(new Peer(connection));
|
||||||
|
|
||||||
peerListeners.stream().forEach(e -> e.onConnectionAuthenticated(connection));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addAuthenticatedPeer(Peer peer) {
|
private void addAuthenticatedPeer(Peer peer) {
|
||||||
Log.traceCall(peer.toString());
|
Log.traceCall(peer.toString());
|
||||||
authenticatedPeers.put(peer.address, peer);
|
Address peerAddress = peer.address;
|
||||||
reportedPeerAddresses.remove(peer.address);
|
authenticatedPeers.put(peerAddress, peer);
|
||||||
firstPeerAdded = !firstPeerAdded && authenticatedPeers.size() == 1;
|
reportedPeerAddresses.remove(peerAddress);
|
||||||
|
|
||||||
peerListeners.stream().forEach(e -> e.onPeerAdded(peer));
|
|
||||||
|
|
||||||
if (firstPeerAdded)
|
|
||||||
peerListeners.stream().forEach(e -> e.onFirstAuthenticatePeer(peer));
|
|
||||||
|
|
||||||
if (!checkIfConnectedPeersExceeds())
|
if (!checkIfConnectedPeersExceeds())
|
||||||
printAuthenticatedPeers();
|
printAuthenticatedPeers();
|
||||||
|
@ -451,13 +455,44 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
}, 1, 2, TimeUnit.MINUTES);
|
}, 1, 2, TimeUnit.MINUTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO needs unit tests
|
||||||
private boolean checkIfConnectedPeersExceeds() {
|
private boolean checkIfConnectedPeersExceeds() {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
if (authenticatedPeers.size() > MAX_CONNECTIONS) {
|
int size = authenticatedPeers.size();
|
||||||
log.trace("We have too many connections open. Lets remove the one which was not active recently.");
|
if (size > MAX_CONNECTIONS_LOW_PRIO) {
|
||||||
List<Connection> authenticatedConnections = networkNode.getAllConnections().stream()
|
Set<Connection> allConnections = networkNode.getAllConnections();
|
||||||
|
log.info("We have {} connections open. Lets remove the passive connections" +
|
||||||
|
" which have not been active recently.", allConnections.size());
|
||||||
|
if (size != allConnections.size())
|
||||||
|
log.warn("authenticatedPeers.size()!=allConnections.size(). There is some inconsistency.");
|
||||||
|
|
||||||
|
List<Connection> authenticatedConnections = allConnections.stream()
|
||||||
|
.filter(e -> e.isAuthenticated())
|
||||||
|
.filter(e -> e.getConnectionType() == ConnectionsType.PASSIVE)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (authenticatedConnections.size() == 0) {
|
||||||
|
log.debug("There are no passive connections for closing. We check if we are exceeding " +
|
||||||
|
"MAX_CONNECTIONS_NORMAL ({}) ", MAX_CONNECTIONS_NORMAL_PRIO);
|
||||||
|
if (size > MAX_CONNECTIONS_NORMAL_PRIO) {
|
||||||
|
authenticatedConnections = allConnections.stream()
|
||||||
|
.filter(e -> e.isAuthenticated())
|
||||||
|
.filter(e -> e.getConnectionType() == ConnectionsType.PASSIVE || e.getConnectionType() == ConnectionsType.ACTIVE)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (authenticatedConnections.size() == 0) {
|
||||||
|
log.debug("There are no passive or active connections for closing. We check if we are exceeding " +
|
||||||
|
"MAX_CONNECTIONS_HIGH ({}) ", MAX_CONNECTIONS_HIGH_PRIO);
|
||||||
|
if (size > MAX_CONNECTIONS_HIGH_PRIO) {
|
||||||
|
authenticatedConnections = allConnections.stream()
|
||||||
.filter(e -> e.isAuthenticated())
|
.filter(e -> e.isAuthenticated())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authenticatedConnections.size() > 0) {
|
||||||
authenticatedConnections.sort((o1, o2) -> o1.getLastActivityDate().compareTo(o2.getLastActivityDate()));
|
authenticatedConnections.sort((o1, o2) -> o1.getLastActivityDate().compareTo(o2.getLastActivityDate()));
|
||||||
log.info("Number of connections exceeds MAX_CONNECTIONS. Current size=" + authenticatedConnections.size());
|
log.info("Number of connections exceeds MAX_CONNECTIONS. Current size=" + authenticatedConnections.size());
|
||||||
Connection connection = authenticatedConnections.remove(0);
|
Connection connection = authenticatedConnections.remove(0);
|
||||||
|
@ -466,14 +501,19 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
connection.shutDown(() -> UserThread.runAfterRandomDelay(() -> checkIfConnectedPeersExceeds(), 100, 500, TimeUnit.MILLISECONDS));
|
connection.shutDown(() -> UserThread.runAfterRandomDelay(() -> checkIfConnectedPeersExceeds(), 100, 500, TimeUnit.MILLISECONDS));
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
log.trace("We don't have too many connections open.");
|
log.warn("That code path should never be reached. (checkIfConnectedPeersExceeds)");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.trace("We only have {} connections open and don't need to close any.", size);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pingPeers() {
|
private void pingPeers() {
|
||||||
Log.traceCall();
|
|
||||||
Set<Peer> connectedPeersList = new HashSet<>(authenticatedPeers.values());
|
Set<Peer> connectedPeersList = new HashSet<>(authenticatedPeers.values());
|
||||||
|
if (!connectedPeersList.isEmpty()) {
|
||||||
|
Log.traceCall();
|
||||||
connectedPeersList.stream()
|
connectedPeersList.stream()
|
||||||
.filter(e -> (new Date().getTime() - e.connection.getLastActivityDate().getTime()) > PING_AFTER_CONNECTION_INACTIVITY)
|
.filter(e -> (new Date().getTime() - e.connection.getLastActivityDate().getTime()) > PING_AFTER_CONNECTION_INACTIVITY)
|
||||||
.forEach(e -> UserThread.runAfterRandomDelay(() -> {
|
.forEach(e -> UserThread.runAfterRandomDelay(() -> {
|
||||||
|
@ -492,11 +532,12 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
});
|
});
|
||||||
}, 1, 10));
|
}, 1, 10));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void trySendGetPeersRequest() {
|
private void trySendGetPeersRequest() {
|
||||||
Log.traceCall();
|
|
||||||
Collection<Peer> peers = authenticatedPeers.values();
|
Collection<Peer> peers = authenticatedPeers.values();
|
||||||
if (!peers.isEmpty()) {
|
if (!peers.isEmpty()) {
|
||||||
|
Log.traceCall();
|
||||||
Set<Peer> connectedPeersList = new HashSet<>(peers);
|
Set<Peer> connectedPeersList = new HashSet<>(peers);
|
||||||
connectedPeersList.stream()
|
connectedPeersList.stream()
|
||||||
.forEach(e -> UserThread.runAfterRandomDelay(() -> {
|
.forEach(e -> UserThread.runAfterRandomDelay(() -> {
|
||||||
|
@ -515,8 +556,6 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, 5, 10));
|
}, 5, 10));
|
||||||
} else {
|
|
||||||
log.info("No peers available for requesting data.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -576,21 +615,6 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Listeners
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public void addPeerListener(PeerListener peerListener) {
|
|
||||||
Log.traceCall();
|
|
||||||
peerListeners.add(peerListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removePeerListener(PeerListener peerListener) {
|
|
||||||
Log.traceCall();
|
|
||||||
peerListeners.remove(peerListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Getters
|
// Getters
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -642,7 +666,7 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
int diff = size - MAX_REPORTED_PEERS;
|
int diff = size - MAX_REPORTED_PEERS;
|
||||||
List<Address> list = new LinkedList<>(getReportedNotConnectedPeerAddresses());
|
List<Address> list = new LinkedList<>(getReportedNotConnectedPeerAddresses());
|
||||||
for (int i = 0; i < diff; i++) {
|
for (int i = 0; i < diff; i++) {
|
||||||
Address toRemove = getAndRemoveRandomItem(list);
|
Address toRemove = getAndRemoveRandomAddress(list);
|
||||||
reportedPeerAddresses.remove(toRemove);
|
reportedPeerAddresses.remove(toRemove);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -668,12 +692,9 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
if (authenticationHandshakes.containsKey(peerAddress))
|
if (authenticationHandshakes.containsKey(peerAddress))
|
||||||
authenticationHandshakes.remove(peerAddress);
|
authenticationHandshakes.remove(peerAddress);
|
||||||
|
|
||||||
boolean contained = reportedPeerAddresses.remove(peerAddress);
|
boolean wasInReportedPeers = reportedPeerAddresses.remove(peerAddress);
|
||||||
Peer disconnectedPeer = authenticatedPeers.remove(peerAddress);
|
Peer disconnectedPeer = authenticatedPeers.remove(peerAddress);
|
||||||
if (disconnectedPeer != null)
|
if (wasInReportedPeers || disconnectedPeer != null)
|
||||||
peerListeners.stream().forEach(e -> e.onPeerRemoved(peerAddress));
|
|
||||||
|
|
||||||
if (contained || disconnectedPeer != null)
|
|
||||||
printAllPeers();
|
printAllPeers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -688,23 +709,24 @@ public class PeerGroup implements MessageListener, ConnectionListener {
|
||||||
// Utils
|
// Utils
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private Address getAndRemoveRandomItem(List<Address> list) {
|
private Optional<Tuple2<Address, Set<Address>>> getRandomNotAuthPeerAndRemainingSet(Set<Address> remainingAddresses) {
|
||||||
Log.traceCall();
|
|
||||||
return list.remove(new Random().nextInt(list.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<Tuple2<Address, Set<Address>>> getRandomItemAndRemainingSet(Set<Address> remainingAddresses) {
|
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
List<Address> list = new ArrayList<>(remainingAddresses);
|
List<Address> list = new ArrayList<>(remainingAddresses);
|
||||||
authenticatedPeers.values().stream().forEach(e -> list.remove(e.address));
|
authenticatedPeers.values().stream().forEach(e -> list.remove(e.address));
|
||||||
if (!list.isEmpty()) {
|
if (!list.isEmpty()) {
|
||||||
Address item = getAndRemoveRandomItem(list);
|
Address item = getAndRemoveRandomAddress(list);
|
||||||
return Optional.of(new Tuple2<>(item, new HashSet<>(list)));
|
return Optional.of(new Tuple2<>(item, new HashSet<>(list)));
|
||||||
} else {
|
} else {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Address getAndRemoveRandomAddress(List<Address> list) {
|
||||||
|
Log.traceCall();
|
||||||
|
return list.remove(new Random().nextInt(list.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void printAllPeers() {
|
public void printAllPeers() {
|
||||||
printAuthenticatedPeers();
|
printAuthenticatedPeers();
|
||||||
printReportedPeers();
|
printReportedPeers();
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
package io.bitsquare.p2p.peers;
|
|
||||||
|
|
||||||
import io.bitsquare.p2p.Address;
|
|
||||||
import io.bitsquare.p2p.network.Connection;
|
|
||||||
|
|
||||||
public interface PeerListener {
|
|
||||||
void onFirstAuthenticatePeer(Peer peer);
|
|
||||||
|
|
||||||
// TODO never used
|
|
||||||
void onPeerAdded(Peer peer);
|
|
||||||
|
|
||||||
// TODO never used
|
|
||||||
void onPeerRemoved(Address address);
|
|
||||||
|
|
||||||
void onConnectionAuthenticated(Connection connection);
|
|
||||||
}
|
|
|
@ -65,10 +65,10 @@ public class SeedNode {
|
||||||
String arg2 = args[2];
|
String arg2 = args[2];
|
||||||
int maxConnections = Integer.parseInt(arg2);
|
int maxConnections = Integer.parseInt(arg2);
|
||||||
checkArgument(maxConnections < 1000, "maxConnections seems to be a bit too high...");
|
checkArgument(maxConnections < 1000, "maxConnections seems to be a bit too high...");
|
||||||
PeerGroup.setMaxConnections(maxConnections);
|
PeerGroup.setMaxConnectionsLowPrio(maxConnections);
|
||||||
} else {
|
} else {
|
||||||
// we keep default a higher connection size for seed nodes
|
// we keep default a higher connection size for seed nodes
|
||||||
PeerGroup.setMaxConnections(50);
|
PeerGroup.setMaxConnectionsLowPrio(50);
|
||||||
}
|
}
|
||||||
if (args.length > 3) {
|
if (args.length > 3) {
|
||||||
String arg3 = args[3];
|
String arg3 = args[3];
|
||||||
|
|
|
@ -65,7 +65,7 @@ public class ProtectedExpirableDataStorage implements MessageListener {
|
||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
Log.traceCall();
|
Log.traceCall();
|
||||||
HashMap<ByteArray, Integer> persisted = storage.initAndGetPersisted(sequenceNumberMap, "sequenceNumberMap");
|
HashMap<ByteArray, Integer> persisted = storage.initAndGetPersisted(sequenceNumberMap, "SequenceNumberMap");
|
||||||
if (persisted != null) {
|
if (persisted != null) {
|
||||||
sequenceNumberMap = persisted;
|
sequenceNumberMap = persisted;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
package io.bitsquare.p2p.storage.messages;
|
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
|
||||||
import io.bitsquare.p2p.Message;
|
|
||||||
import io.bitsquare.p2p.storage.data.ProtectedData;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
public final class DataExchangeMessage implements Message {
|
|
||||||
// That object is sent over the wire, so we need to take care of version compatibility.
|
|
||||||
private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
|
|
||||||
private final int networkId = Version.NETWORK_ID;
|
|
||||||
|
|
||||||
public final HashSet<ProtectedData> set;
|
|
||||||
|
|
||||||
public DataExchangeMessage(HashSet<ProtectedData> set) {
|
|
||||||
this.set = set;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int networkId() {
|
|
||||||
return networkId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (!(o instanceof DataExchangeMessage)) return false;
|
|
||||||
|
|
||||||
DataExchangeMessage that = (DataExchangeMessage) o;
|
|
||||||
|
|
||||||
return !(set != null ? !set.equals(that.set) : that.set != null);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return set != null ? set.hashCode() : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "GetDataResponse{" +
|
|
||||||
"networkId=" + networkId +
|
|
||||||
", set=" + set +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -59,7 +59,7 @@ public class P2PServiceTest {
|
||||||
|
|
||||||
LocalhostNetworkNode.setSimulateTorDelayTorNode(10);
|
LocalhostNetworkNode.setSimulateTorDelayTorNode(10);
|
||||||
LocalhostNetworkNode.setSimulateTorDelayHiddenService(100);
|
LocalhostNetworkNode.setSimulateTorDelayHiddenService(100);
|
||||||
PeerGroup.setMaxConnections(8);
|
PeerGroup.setMaxConnectionsLowPrio(8);
|
||||||
|
|
||||||
keyRing1 = new KeyRing(new KeyStorage(dir1));
|
keyRing1 = new KeyRing(new KeyStorage(dir1));
|
||||||
keyRing2 = new KeyRing(new KeyStorage(dir2));
|
keyRing2 = new KeyRing(new KeyStorage(dir2));
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
package io.bitsquare.p2p.routing;
|
package io.bitsquare.p2p.routing;
|
||||||
|
|
||||||
import io.bitsquare.common.util.Profiler;
|
|
||||||
import io.bitsquare.p2p.Address;
|
import io.bitsquare.p2p.Address;
|
||||||
import io.bitsquare.p2p.P2PService;
|
import io.bitsquare.p2p.P2PService;
|
||||||
import io.bitsquare.p2p.P2PServiceListener;
|
import io.bitsquare.p2p.P2PServiceListener;
|
||||||
import io.bitsquare.p2p.network.Connection;
|
|
||||||
import io.bitsquare.p2p.network.LocalhostNetworkNode;
|
import io.bitsquare.p2p.network.LocalhostNetworkNode;
|
||||||
import io.bitsquare.p2p.peers.AuthenticationListener;
|
|
||||||
import io.bitsquare.p2p.peers.PeerGroup;
|
import io.bitsquare.p2p.peers.PeerGroup;
|
||||||
import io.bitsquare.p2p.seed.SeedNode;
|
import io.bitsquare.p2p.seed.SeedNode;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
@ -36,7 +33,7 @@ public class PeerGroupTest {
|
||||||
public void setup() throws InterruptedException {
|
public void setup() throws InterruptedException {
|
||||||
LocalhostNetworkNode.setSimulateTorDelayTorNode(50);
|
LocalhostNetworkNode.setSimulateTorDelayTorNode(50);
|
||||||
LocalhostNetworkNode.setSimulateTorDelayHiddenService(8);
|
LocalhostNetworkNode.setSimulateTorDelayHiddenService(8);
|
||||||
PeerGroup.setMaxConnections(100);
|
PeerGroup.setMaxConnectionsLowPrio(100);
|
||||||
|
|
||||||
seedNodes = new HashSet<>();
|
seedNodes = new HashSet<>();
|
||||||
if (useLocalhost) {
|
if (useLocalhost) {
|
||||||
|
@ -213,7 +210,8 @@ public class PeerGroupTest {
|
||||||
// node1 -> node2 PeersMessage
|
// node1 -> node2 PeersMessage
|
||||||
|
|
||||||
// first authentication from seedNode2 to seedNode1, then from seedNode1 to seedNode2
|
// first authentication from seedNode2 to seedNode1, then from seedNode1 to seedNode2
|
||||||
CountDownLatch latch1 = new CountDownLatch(2);
|
//TODO
|
||||||
|
/* CountDownLatch latch1 = new CountDownLatch(2);
|
||||||
AuthenticationListener routingListener1 = new AuthenticationListener() {
|
AuthenticationListener routingListener1 = new AuthenticationListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onConnectionAuthenticated(Connection connection) {
|
public void onConnectionAuthenticated(Connection connection) {
|
||||||
|
@ -274,12 +272,13 @@ public class PeerGroupTest {
|
||||||
seedNode1.shutDown(() -> shutDownLatch.countDown());
|
seedNode1.shutDown(() -> shutDownLatch.countDown());
|
||||||
seedNode2.shutDown(() -> shutDownLatch.countDown());
|
seedNode2.shutDown(() -> shutDownLatch.countDown());
|
||||||
seedNode3.shutDown(() -> shutDownLatch.countDown());
|
seedNode3.shutDown(() -> shutDownLatch.countDown());
|
||||||
shutDownLatch.await();
|
shutDownLatch.await();*/
|
||||||
}
|
}
|
||||||
|
|
||||||
//@Test
|
//@Test
|
||||||
public void testAuthenticationWithDisconnect() throws InterruptedException {
|
public void testAuthenticationWithDisconnect() throws InterruptedException {
|
||||||
LocalhostNetworkNode.setSimulateTorDelayTorNode(0);
|
//TODO
|
||||||
|
/* LocalhostNetworkNode.setSimulateTorDelayTorNode(0);
|
||||||
LocalhostNetworkNode.setSimulateTorDelayHiddenService(0);
|
LocalhostNetworkNode.setSimulateTorDelayHiddenService(0);
|
||||||
SeedNode seedNode1 = getAndStartSeedNode(8001);
|
SeedNode seedNode1 = getAndStartSeedNode(8001);
|
||||||
SeedNode seedNode2 = getAndStartSeedNode(8002);
|
SeedNode seedNode2 = getAndStartSeedNode(8002);
|
||||||
|
@ -340,12 +339,13 @@ public class PeerGroupTest {
|
||||||
CountDownLatch shutDownLatch = new CountDownLatch(2);
|
CountDownLatch shutDownLatch = new CountDownLatch(2);
|
||||||
seedNode1.shutDown(() -> shutDownLatch.countDown());
|
seedNode1.shutDown(() -> shutDownLatch.countDown());
|
||||||
seedNode2.shutDown(() -> shutDownLatch.countDown());
|
seedNode2.shutDown(() -> shutDownLatch.countDown());
|
||||||
shutDownLatch.await();
|
shutDownLatch.await();*/
|
||||||
}
|
}
|
||||||
|
|
||||||
//@Test
|
//@Test
|
||||||
public void testAuthenticationWithManyNodes() throws InterruptedException {
|
public void testAuthenticationWithManyNodes() throws InterruptedException {
|
||||||
int authentications = 0;
|
//TODO
|
||||||
|
/* int authentications = 0;
|
||||||
int length = 3;
|
int length = 3;
|
||||||
SeedNode[] nodes = new SeedNode[length];
|
SeedNode[] nodes = new SeedNode[length];
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
|
@ -379,7 +379,7 @@ public class PeerGroupTest {
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
nodes[i].shutDown(() -> shutDownLatch.countDown());
|
nodes[i].shutDown(() -> shutDownLatch.countDown());
|
||||||
}
|
}
|
||||||
shutDownLatch.await();
|
shutDownLatch.await();*/
|
||||||
}
|
}
|
||||||
|
|
||||||
private SeedNode getAndStartSeedNode(int port) throws InterruptedException {
|
private SeedNode getAndStartSeedNode(int port) throws InterruptedException {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue